无序线程问题

时间:2010-08-12 07:53:16

标签: c# .net multithreading locking

我曾在here询问有关锁的问题,人们回答我的锁实现没有问题。但我抓住了问题。这是相同的锁实现,我得到了奇怪的结果。我希望看到数字从1开始,但它从5开始。例如,在下面。

class Program
{
    static object locker = new object();
    static void Main(string[] args)
    {
        for (int j = 0; j < 100; j++)
        {
            (new Thread(new ParameterizedThreadStart(dostuff))).Start(j);
        }
        Console.ReadKey();
    }
    static void dostuff(dynamic input)
    {
        lock (locker)
        {
            Console.WriteLine(input);
        }
    }
}

3 个答案:

答案 0 :(得分:3)

代码很好。但是你不能保证执行线程的顺序。当我运行代码时,我得到:

0 1 3 五 2 4 6 10 9 11 7 12 8 等

如果您需要按指定的顺序运行线程,可以考虑使用ThreadPool.QueueUserWorkItem代替。

class Program
{
      static object locker = new object();
      static EventWaitHandle clearCount 
          =new EventWaitHandle(false, EventResetMode.ManualReset);
  static void Main(string[] args)
  {
    for (int j = 0; j < 100; j++)
    {
       ThreadPool.QueueUserWorkItem(dostuff, j);
    }
    clearCount.WaitOne();
  }
  static void dostuff(dynamic input)
  {
    lock (locker)
    {
      Console.WriteLine(input);
          if (input == 99) clearCount.Set();
     }
   }
}

答案 1 :(得分:1)

将锁放在您放置的位置是没有意义的,因为您没有锁定更改多个线程共享的值的代码。您锁定的代码部分根本不会更改任何变量。

数字出现故障的原因是因为线程无法保证以任何特定顺序启动,除非您执行@Mikael Svenson建议的操作。

有关共享变量的示例,如果您使用此代码:

class Program
{
    static object locker = new object();
    static int count=0;

    static void Main(string[] args)
    {
        for (int j = 0; j < 100; j++)
        {
            (new Thread(new ParameterizedThreadStart(dostuff))).Start(j);
        }
        Console.ReadKey();
    }

    static void dostuff(object Id)
    {
        lock (locker)
        {
            count++;
            Console.WriteLine("Thread {0}: Count is {1}", Id, count);
        }
    }
}

您可能会看到线程编号不按顺序排列,但计数是。如果删除lock语句,则计数也不会按顺序排列。

答案 2 :(得分:1)

这里有一些问题和错误的假设。

  • 不建议以这种方式创建100个线程。
  • 线程不会按照它们的启动顺序执行。
  • 将锁置于您拥有它的位置将有效地序列化线程的执行,立即消除您希望通过使用线程获得的任何优势。

使用的最佳方法是将问题划分为单独的独立块,这些块可以使用尽可能少的线程同步来同时计算。这些分区应该在很小且相当静态的线程上执行。您可以使用ThreadPoolParallelTask类来执行此操作。

我使用Parallel.For方法包含了一个示例模式。为了使示例易于理解,假设您有一个要克隆的对象列表并将其放入单独的列表中。让我们假设克隆操作很昂贵,并且您希望并行化克隆许多对象。这是你怎么做的。请注意lock关键字的展示位置和使用限制。

public static void Main()
{
  List<ICloneable> original = GetCloneableObjects();
  List<ICloneable> copies = new List<ICloneable>();
  Parallel.For(0, 100,
    i =>
    {
      ICloneable cloneable = original[i];
      ICloneable copy = cloneable.Clone();
      lock (copies)
      {
        copies.Add(copy);
      }
    });
}