数组和线程安全访问

本文关键字:访问 安全 线程 数组 | 更新日期: 2023-09-27 18:36:46

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

我在互联网上广泛搜索了一下,几乎没有发现关于这个主题的信息,一切似乎都是特定的场景(例如,这个数组,这两个线程像这样访问线程安全,等等)。我真的很希望有人可以回答我在顶部列出的问题,或者有人可以指出一份解释所述项目的好文件。

编辑:在MSDN上环顾四周后,我找到了ArrayList类。使用 sync 方法时,它将返回给定列表的线程安全包装器。在列表中设置数据时(即 list1[someNumber] = otherNumber;)包装器是否自动负责锁定列表,还是仍需要锁定列表?

数组和线程安全访问

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

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

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

1) 使用由框架管理为可锁定的特定 .NET 对象(如 Scorpion-Prince 指向 SyncdCollection 的链接)

2) 使用 [MethodImpl

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

3) 使用 lock 语句隔离正在执行潜在危险操作的特定代码行

哪种方法是最好的,实际上取决于您的情况。

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

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

例如,一般来说,假设您有线程 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"。

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

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

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

如果数组被命名为public并且static关键字(不能保证即时),则数组是线程安全的,因为System.Array实现了ICollection接口,该接口定义了一些同步方法以支持同步机制。

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

前任:

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