关于.NET线程的访谈问题

时间:2010-01-22 13:11:53

标签: multithreading

您能描述两种同步多线程写访问的方法吗? 在一个班级成员?

请任何人帮助我这意味着什么,以及正确的答案。

3 个答案:

答案 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中的一个(例如lockMutexReaderWriterLockSlim等)锁定对类成员的访问权限。只有一个线程可以一次处理它。

2)将写入操作推送到队列中并使用单个线程处理该队列。正如Thorarin指出的那样,如果它不是线程安全的,你仍然必须同步对队列的访问,但是对于复杂的写操作来说它是值得的。

还有其他技巧。有些(如Interlocked)仅限于特定的数据类型,甚至更多(如Non-blocking synchronizationPart 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)

有几种方法,其中有几种是先前提到的。

  1. ReaderWriterLockSlim是我首选的方法。这为您提供了一种数据库类型的锁定,并允许升级(虽然上次我查看时MSDN的语法不正确,但非常不明显)
  2. 锁定声明。您将读取视为写入,只是阻止访问变量
  3. 联锁行动。这将对原子步骤中的值类型执行操作。这可以用于无锁线程(真的不建议这样做)
  4. Mutexes和Semaphores(尚未使用这些)
  5. 监控报表(这主要是锁定关键字的工作原理)
  6. 虽然我并不打算诋毁其他答案,但我不相信任何不使用这些技巧的东西。如果我忘记了,我道歉。