数组和线程安全访问

时间:2012-06-08 15:52:54

标签: c# .net arrays

如果我有一个可以/将在任何给定时间点被多个线程访问的数组,究竟是什么导致它是非线程安全的,以及为确保该数组将是线程所采取的步骤在大多数情况下安全吗?

我在互联网上广泛关注并且发现很少或没有关于这个主题的信息,一切似乎都是特定的场景(例如,这个数组,这两个线程是通过线程安全的,这样的,以及)。我真的希望有人可以回答我在顶部提出的问题,或者是否有人可以指出一份解释所述项目的好文件。

编辑: 在MSDN上查看之后,我找到了ArrayList类。使用synchronize方法时,它返回给定列表的线程安全包装器。在列表中设置数据时(例如list1 [someNumber] = anotherNumber;)包装器会自动锁定列表,还是需要锁定它?

4 个答案:

答案 0 :(得分:6)

当两个线程访问完全相同的资源(例如,不是本地副本,但实际上是相同资源的相同副本)时,可能会发生许多事情。在最明显的情况下,如果线程#1正在访问资源而线程#2在读取过程中将其更改,则可能会发生一些不可预测的行为。即使使用像整数这样简单的东西,也可能会出现逻辑错误,所以试着想象一下因不正确地使用更复杂的东西而导致的恐怖,比如声明为静态的数据库访问类。

处理此问题的经典方法是锁定敏感资源,这样一次只有一个线程可以使用它。因此,在上面的示例中,线程#1将请求锁定资源并被授予它,然后继续读取它需要读取的内容。线程#2将在读取中间并请求锁定资源,但被拒绝并被告知等待,因为线程#1正在使用它。当线程#1完成时,它会释放锁定,并且线程#2可以继续。

还有其他情况,但这说明了最基本的问题和解决方案之一。在C#中,您可以:

1)使用框架可管理的特定.NET对象(如Scorpion-Prince的link to SynchronizedCollection

2)使用[MethodImpl(MethodImplOptions.Synchronized)]来指示执行危险操作的特定方法一次只能由一个线程使用

3)使用lock statement隔离正在执行某些潜在危险的特定代码行

最好的方法取决于你的情况。

答案 1 :(得分:4)

  

如果我有一个可以/将被多个线程访问的数组   任何给定的时间点,究竟是什么导致它是非线程安全的,   以及确保阵列的步骤是什么   在大多数情况下线程安全?

一般而言,数组不是线程安全的这一事实是,如果您不同步对数组的访问,则两个或多个线程可能正在修改数组的内容。

一般来说,例如,让我们假设你有线程1做这项工作:

for (int i = 0; i < array.Length; i++)
{
   array[i] = "Hello";
}

线程2完成这项工作(在同一个共享阵列上)

for (int i = 0; i < array.Length; i++)
{
   array[i] = "Goodbye";
}

没有任何同步线程的内容,因此您的结果将取决于哪个线程首先赢得比赛。它可以是“Hello”或“Goodbye”,以某种随机顺序排列,但总是至少是“Hello”或“Goodbye”。

CLR保证字符串'Hello'或'Goodbye'的实际是原子的。也就是说,值'Hello'的写入不能被试图写'Goodbye'的线程打断。一个必须在另一个之前或之后发生,而不是在两者之间。

因此,您需要创建某种同步机制以防止数组相互踩踏。您可以通过在C#中使用lock语句来完成此操作。

答案 2 :(得分:2)

C#3.0及更高版本提供了一个名为SynchronizedCollection的泛型集合类,它“提供了一个线程安全的集合,其中包含泛型参数指定的类型的对象作为元素。”

答案 3 :(得分:0)

如果数组名为publicstatic个关键字,则无法保证即时,数组是线程安全的 - 因为System.Array实现了ICollection接口,该接口定义了一些同步方法支持同步机制。

但是,编译枚举数组的项目并不安全,开发人员应该实现lock语句,以确保在数组枚举期间数组没有变化。

EX:

Array arrThreadSafe = new string[] {"We", "are", "safe"};
lock(arrThreadSafe.SyncRoot)
{
   foreach (string item in arrThreadSafe)
   {
     Console.WriteLine(item);
   }
}