首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >线程安全:锁与引用

线程安全:锁与引用
EN

Stack Overflow用户
提问于 2014-06-06 18:44:35
回答 3查看 352关注 0票数 5

我有一个C#程序,它有一个列表,可以在单独的线程中进行写入和读取。写入是由用户发起的,并且可以在任意时间点更改数据。读取在一个常数循环中运行。读取是否丢失任何给定循环中的数据并不重要,只要它接收的数据是有效的,并且在未来的循环中得到新的数据。

在考虑了ConcurrentBag之后,出于各种原因(简单性就是其中之一),我决定使用锁。在实现锁之后,一位同事向我提到,使用临时引用来指向内存中的旧列表也同样有效,但我担心如果同时进行新的分配和引用分配会发生什么情况。

Q:线程下面的临时引用示例是否安全?

更新:用户输入提供DoStuff()中使用的字符串列表。您可以将这些字符串看作常量的定义,因此,对于未来的循环,字符串需要持久化。它们不会在DoStuff()中被删除,只会被读取。UserInputHandler是唯一将更改此列表的线程,而DoStuff()则是唯一将从该列表中读取的线程。没有其他人能接触到它。

此外,我知道并发命名空间,并在其他项目中使用了其中的大多数集合,但是,由于它们添加的额外代码复杂性(即ConcurrentBag没有简单的Clear()函数等),我选择不在这里使用它们。在这种情况下,简单的锁就足够了。问题只是下面的第二个例子是否是线程安全的。

代码语言:javascript
复制
static List<string> constants = new List<string>();

//Thread A
public void UserInputHandler(List<string> userProvidedConstants)
{
    lock(items)
    {
        items.Clear();
        foreach(var constant in userProvidedConstants)
        {
            constants.Add(constant);
        }
    }
}

//Thread B
public void DoStuff()
{
    lock(items)
    {
        //Do read only actions with items here
        foreach(var constant in constants)
        {
            //readonly actions
        }
    }
}

参考

代码语言:javascript
复制
static List<string> constants = new List<string>();

//Thread A
public void UserInputHandler(List<string> userProvidedConstants)
{
    lock(items)
    {
        items = new List<string>();
        foreach(var constant in userProvidedConstants)
        {
            constants.Add(constant);
        }
    }
}

//Thread B
public void DoStuff()
{
    var constantsReference = constants;

    //Do read only actions with constantsReference here
    foreach(var constant in constantsReference)
    {
        //readonly actions
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-06-06 18:47:13

没有锁这是不安全的。在这种情况下,复制对列表的引用并不能真正为您做任何事情。当前正在迭代的列表仍然很有可能在迭代时在另一个线程中发生变异,从而导致各种可能的错误。

票数 8
EN

Stack Overflow用户

发布于 2014-06-06 19:04:21

我想你要找的是BlockingCollection。要开始使用它,请查看以下链接:

http://msdn.microsoft.com/en-us/library/dd997371%28v=vs.110%29.aspx

下面是一个使用BlockingCollection的示例。直到有可用的项时,ThreadB才会开始枚举BlockingCollection,当项目用完时,它将停止枚举,直到有更多的项目可用为止(或者直到IsCompleted属性返回true为止)。

代码语言:javascript
复制
private static readonly BlockingCollection<int> Items = new BlockingCollection<int>();

//ThreadA
public void LoadStuff()
{
    Items.Add(1);
    Items.Add(2);
    Items.Add(3);
}

//ThreadB
public void DoStuff()
{
    foreach (var item in Items.GetConsumingEnumerable())
    {
        //Do stuff here
    }
}
票数 0
EN

Stack Overflow用户

发布于 2014-06-06 19:05:08

无锁是危险的,不能携带。不要这样做。如果您需要阅读如何进行无锁操作,则可能不应该这样做。

我想我没能理解这个问题。我有一种奇怪的印象,那就是名单只被添加到或仅仅是最近的版本中,这才是最重要的。我不知道当他显式地显示一个"clear()“调用时是如何做到这一点的。

我为混乱而道歉。

这段代码是有争议的,请自己承担风险,但我确信它应该在x86/x64上工作,但对ARM却毫无头绪

你可以做这样的事

代码语言:javascript
复制
//Suggested to just use volatile instead of memorybarrier
    static volatile T _MyList = new ReadOnlyList<T>();

    void Load(){
    T LocalList = _MyList.Copy();
    LocalList.Add(1);
    LocalList.Add(2);
    LocalList.Add(3);

    _MyList = LocalList.ReadOnly(); //Making it more clear

    }

    DoStuff(){
    T LocalList = _MyList;

    foreach(t tmp in LocalList)
    }

对于繁重的读取工作负载,这应该很好。如果您有多个修改_MyList的编写器,则需要找到同步它们的方法。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24088662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档