如何在C#中使对象线程安全?

4

我有一个应用程序,大多数情况下需要进行线程处理。大部分时间我会遇到错误或错误的值,因为对象在每个线程执行之前被更新。

您有任何建议如何使对象线程安全,并确保每个线程都具有正确的对象?我应该将我的变量设置为static吗?


3
你应该先从书开始学习。总的来说,多进程编程是一件难以做到完美的事情。 - Jeff Mercado
3个回答

20
首先,您应该绘制一个“发生在之前图”,以解决多线程问题。如果您无法绘制设计图,则说明它太复杂了。
例如,这是一个“发生在之前图”,表示一个方法接受两个int数组并输出所有元素的总和。

enter image description here

一旦你有了happens-before图,就很容易看出某些事情必须在另一些事情之前发生,但更重要的是它向你展示了某些事情不必在另一些事情之前发生。

在这个例子中,你可以同时得到array1和array2的总和。你也可以同时得到sum1和sum2,因为它们彼此不依赖。将这些总和加到TotalSum1中可以按任意顺序完成(但你需要在添加步骤周围加锁,因为你不能与其他操作同时进行)。

C# .net 4.0对于并行编程有很多有用的能力。

我推荐这本书Parallel Programming with Microsoft .Net——只需使用左侧的书签导航即可。它涵盖了循环、任务、聚合、未来和管道的并行模式。

在上面的示例中,我会使用Task1获取Array1和Task2获取Array2,然后在两个任务中都使用并行循环中内置的聚合模式来计算数组总和。为了累加子总数,我需要使用锁定或Interlocked.Add,然后等待任务完成并返回结果。
有用的模式:
- 任务 - 并行循环 - 生产者/消费者 - 管道 - 工作列表 - MapReduce 有用的工具:
  • 任务并行库
    • Parallel.For
    • Parallel.Foreach,
    • Tasks
  • PLinq
  • 并发数据结构
    • ConcurrentQueue
    • 等。
  • 锁定
    • 'lock' 关键字
    • Monitor(与 'lock' 功能相同)
    • Semaphores
    • 自旋锁
    • 读/写锁
  • 硬件原子操作
    • Interlocked.Increment
    • Interlocked.Add
    • Interlocked.CompareAndSwap
    • 对机器字长变量(如 int)的赋值
  • 消息传递
    • MPI
    • 屏障
    • 等。

基本上,先理解你的问题,然后选择工具/模式来解决它。


10

需要确保线程安全的资源,可以考虑使用锁语句(lock statement)。

http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx

class Account
{
    decimal balance;
    private Object thisLock = new Object();

    public void Withdraw(decimal amount)
    {
        lock (thisLock)
        {
            if (amount > balance)
            {
                throw new Exception("Insufficient funds");
            }
            balance -= amount;
        }
    }
}

这将是我的第一步。您还可以查看 Monitor 类。

http://msdn.microsoft.com/en-us/library/system.threading.monitor.aspx

这是保护并发操作期间资源的两种基本方法。还有其他许多方法,例如互斥锁、信号量、条件读/写锁等。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接