C#基本多线程问题 - 如何使用锁来解决竞争条件

时间:2017-11-07 01:13:01

标签: c# multithreading

public class ThreadingIssue
{
    int accum;

    public void Squaring(int x)
    {
        // Creates a random pause to check for thread
        int pauseFor = rnd.Next(1, 10);
        Thread.Sleep(pauseFor);

        accum += x*x;

     }

     public void DoSquaring()
     {
       accum = 0;
       for (int i = 1; i <= 100; i++)
       {
          threads.Add(new Thread(() => Squaring(i)));
          threads[i - 1].Start();
       }
      }

基本上我想每次都得到338350作为答案,但显然我没有,因为存在线程问题。我试图创建一个像这样的对象:

 private System.Object lockThis = new System.Object();

然后像这样锁定accum变量:

 lock (lockThis)
 {
    accum += x*x;
 }

但这并不能解决问题。我错过了什么/做错了什么?

3 个答案:

答案 0 :(得分:7)

你的for循环有问题。

for (int i = 1; i <= 100; i++)
{
    threads.Add(new Thread(() => Squaring(i)));
    threads[i - 1].Start();
}

在启动每个线程时,lambda i内部使用循环变量() => Squaring(i),但是在创建任何线程时,循环已完成,i的值为{{ 1}}。

要解决此问题,您需要在循环中捕获101的副本并使用它。

试试这个:

i

您还需要在for (int i = 1; i <= 100; i++) { int j = i; threads.Add(new Thread(() => Squaring(j))); threads[i - 1].Start(); } 行附近lock

accum += x * x;

答案 1 :(得分:2)

我知道这并不是严格要求的,但为了引导你朝着正确的方向前进,这项任务完全适用于非常小的Linq和Tasks.Parallel:

var bag = new ConcurrentBag<KeyValuePair<int, int>>();

Parallel.For(1, 101, 
    i => 
    {
        bag.Add(new KeyValuePair<int, int>(i, i * i));
    });

var squares = bag.ToDictionary(pair => pair.Key, pair => pair.Value);

Console.WriteLine("Square of 56 is " + squares[56].ToString());
Console.WriteLine("Sum of all squares is " + squares.Sum(pair => pair.Value).ToString());

这利用了相对较新的System.Collections.Concurrent命名空间来完全消除了手动管理锁的需要,并利用System.Threading.Tasks命名空间来并行化工作负载。循环使用许多线程填充集合,让操作系统决定它将产生多少个线程以保持系统响应,并且在它被强制转换为字典后,您可以随意执行任何操作。您还可以将ParallelOptions实例传递给Parallel.For()调用,以指定任务是短期还是长期运行,以告诉操作系统应如何对其进行优先级排序,并指定最大并行度。任务。

请确保您没有在表单的主要线程上使用Tasks.Parallel。

答案 2 :(得分:1)

首先,这样做:

public class ThreadingIssue
{
    int accum;
    private readonly object _syncLock = new object();

    public void Squaring(int x)
    {
        // Creates a random pause to check for thread
        int pauseFor = rnd.Next(1, 10);
        Thread.Sleep(pauseFor);

        lock( _syncLock ) 
        {
           accum += x*x;
        }
     }

     public void DoSquaring()
     {
       accum = 0;
       for (int i = 1; i <= 100; i++)
       {
          int number = i;
          threads.Add(new Thread(() => Squaring(number)));
          threads[i - 1].Start();
       }
      }
    }