您能描述两种同步多线程写访问的方法吗? 在一个班级成员?
请任何人帮助我这意味着什么,以及正确的答案。
答案 0 :(得分:13)
当您在C#中更改数据时,可能会将看似单个操作的内容编译为多个指令。参加以下课程:
public class Number {
private int a = 0;
public void Add(int b) {
a += b;
}
}
构建它时,您将获得以下IL代码:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: dup
// Pushes the value of the private variable 'a' onto the stack
IL_0003: ldfld int32 Simple.Number::a
// Pushes the value of the argument 'b' onto the stack
IL_0008: ldarg.1
// Adds the top two values of the stack together
IL_0009: add
// Sets 'a' to the value on top of the stack
IL_000a: stfld int32 Simple.Number::a
IL_000f: ret
现在,假设你有一个Number
对象,两个线程调用它的Add
方法:
number.Add(2); // Thread 1
number.Add(3); // Thread 2
如果您希望结果为5(0 + 2 + 3),则表示存在问题。您不知道这些线程何时执行其指令。在执行IL_0003
(实际更改成员变量)之前,两个线程都可以执行IL_000a
(将零推到堆栈上),然后得到这个:
a = 0 + 2; // Thread 1
a = 0 + 3; // Thread 2
完成'胜利'的最后一个帖子,在流程结束时,a
是2或3而不是5。
因此,您必须确保在另一组之前完成一整套指令。为此,您可以:
1)使用众多.NET synchronization primitives中的一个(例如lock
,Mutex
,ReaderWriterLockSlim
等)锁定对类成员的访问权限。只有一个线程可以一次处理它。
2)将写入操作推送到队列中并使用单个线程处理该队列。正如Thorarin指出的那样,如果它不是线程安全的,你仍然必须同步对队列的访问,但是对于复杂的写操作来说它是值得的。
还有其他技巧。有些(如Interlocked
)仅限于特定的数据类型,甚至更多(如Non-blocking synchronization和Part 4 of Joseph Albahari's Threading in C#中讨论的那些),尽管它们更复杂:谨慎处理它们
答案 1 :(得分:12)
在多线程应用程序中,有许多情况下同时访问相同的数据可能会导致问题。在这种情况下,需要同步以保证任何时候只有一个线程可以访问。
我认为他们的意思是使用lock
-statement(或VB.NET中的SyncLock
)而不是Monitor
。
您可能希望read this page获取示例并理解该概念。但是,如果您没有使用多线程应用程序设计的经验,如果您的新雇主让您参加测试,很可能会很快显现出来。这是一个相当复杂的主题,有许多可能的陷阱,例如deadlock。
还有一个不错的MSDN page on the subject。
可能还有其他选项,具体取决于成员变量的类型以及如何更改。例如,可以使用Interlocked。递增方法来增加整数。
作为问题的练习和演示,尝试编写一个启动5个并发线程的应用程序,每个线程增加一个共享计数器一百万次。计数器的预期最终结果将是500万,但这(可能)不是你最终会得到的结果:)
编辑:自己快速实施(download)。样本输出:
Unsynchronized counter demo:
expected counter = 5000000
actual counter = 4901600
Time taken (ms) = 67
Synchronized counter demo:
expected counter = 5000000
actual counter = 5000000
Time taken (ms) = 287
答案 2 :(得分:0)
有几种方法,其中有几种是先前提到的。
虽然我并不打算诋毁其他答案,但我不相信任何不使用这些技巧的东西。如果我忘记了,我道歉。