Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 锁是否确保列表元素的线程安全?_C#_Multithreading_List_Reference_Locking - Fatal编程技术网

C# 锁是否确保列表元素的线程安全?

C# 锁是否确保列表元素的线程安全?,c#,multithreading,list,reference,locking,C#,Multithreading,List,Reference,Locking,据我理解,他们没有。请确认 假设我有一个Processor对象,它包含一个列表,其中T是引用类型 UI线程实例化Processor对象,并定期调用它以获取列表 processor对象还启动了一个任务,该任务在构建时引用了列表 锁用于在getter和任务中保留对列表的访问权,以确保它们以独占方式访问列表 公共类处理器 { 私有列表_List=新列表(); 私有对象_listLock=新对象(); 公共处理器() { //填充_列表 任务t=新任务(流程); t、 Start(); } 公共列表Ge

据我理解,他们没有。请确认

假设我有一个Processor对象,它包含一个列表,其中T是引用类型

UI线程实例化Processor对象,并定期调用它以获取列表

processor对象还启动了一个任务,该任务在构建时引用了列表

锁用于在getter和任务中保留对列表的访问权,以确保它们以独占方式访问列表

公共类处理器
{
私有列表_List=新列表();
私有对象_listLock=新对象();
公共处理器()
{
//填充_列表
任务t=新任务(流程);
t、 Start();
}
公共列表GetList()
{
锁(_listLock)
{
返回列表;
}
}
私有无效进程()
{
同时(!完成处理)
{
锁(_listLock)
{
//访问和修改列表项
}
睡眠(…);
}
}
}
但是,即使列表在getter中被锁定,并且它返回了列表引用而没有问题,处理器启动的任务在获取锁时仍然在修改引用类型列表元素

列表中的元素仍可能因处理器的任务而发生更改,在UI线程中访问它们将不是线程安全的

如果我是正确的,一个明显的解决方案是让getter返回一个新的列表,其中包含列表元素的深度副本

public List<T> GetList()
{
  lock(_listLock)
  {
    return _list.Select(t => t.Clone()).ToList();
  }
}
public List GetList()
{
锁(_listLock)
{
return _list.Select(t=>t.Clone()).ToList();
}
}
您还可以做什么?

您的GetList()并不像您认为的那样:

public List<T> GetList()
  {
    lock(_listLock)
    {
      return _list;
    }
  }
public List GetList()
{
锁(_listLock)
{
返回列表;
}
}
由于GetList()只返回对_list的引用,所以lock()除了防止两个线程同时获取对_list的引用之外,什么都不做,这无论如何都不是问题

问题是,您正在将对列表对象的引用传递回UI线程,并且当UI线程在元素上迭代时,引用指向的列表中的元素可能随时发生更改,这是不好的

您可以向UI线程公开锁对象,但这意味着您的UI需要在更新列表时进行阻止,这通常是不可取的(阻止UI线程会降低用户体验)

根据您的UI对列表的处理方式,最好是获取列表的不可变快照并将其返回到UI

如果您希望使UI与基础列表数据的更改保持同步,那么您采取的方法实际上取决于UI技术。

您的GetList()并没有像您认为的那样:

public List<T> GetList()
  {
    lock(_listLock)
    {
      return _list;
    }
  }
public List GetList()
{
锁(_listLock)
{
返回列表;
}
}
由于GetList()只返回对_list的引用,所以lock()除了防止两个线程同时获取对_list的引用之外,什么都不做,这无论如何都不是问题

问题是,您正在将对列表对象的引用传递回UI线程,并且当UI线程在元素上迭代时,引用指向的列表中的元素可能随时发生更改,这是不好的

您可以向UI线程公开锁对象,但这意味着您的UI需要在更新列表时进行阻止,这通常是不可取的(阻止UI线程会降低用户体验)

根据您的UI对列表的处理方式,最好是获取列表的不可变快照并将其返回到UI


如果您希望使UI与底层列表数据的更改保持同步,那么您采取的方法实际上取决于UI技术。

锁(以您现有的方式使用)将无法确保线程安全

如果您正在尝试线程安全性,请考虑使用.NET CLR中可用的一个。


您会注意到没有线程安全的IList。线程安全列表的概念本身没有多大意义。但是,通过一些小的设计更改,您可以很容易地使用or之类的锁。

以您现有的方式使用的锁将无法确保线程安全

如果您正在尝试线程安全性,请考虑使用.NET CLR中可用的一个。


您会注意到没有线程安全的IList。线程安全列表的概念本身没有多大意义。但是,通过一些小的设计更改,您可以很容易地使用or之类的工具。

不是答案,而只是调用
ToList()
创建列表的副本。您可以仅公开所使用的
列表中的方法,而不是整个列表。通过这种方式,您可以在访问和修改项目时使用锁定。您是否清楚这样一个事实,即在修改列表元素时,锁定列表不会提供安全性?不是答案,只是调用
ToList()
创建列表的副本。您只能公开您使用的
列表中的方法,而不能公开整个列表。通过这种方式,您可以在访问和修改项目时使用锁定。您是否清楚这样一个事实:在修改列表元素时,锁定列表不会提供安全性?