1

我有一个接受List<int>被调用的方法DoWork。我有一个巨大 List<int> Ids的. 我将庞大的列表拆分为 4 个子列表:

List<List<int>> t = (List<List<int>>)SplitColumn<int>(Ids);

(从将列表拆分为子列表SplitColumn的答案略有修改)。

我暂停了程序并t用调试器检查,它是四个列表,完全按照我的预期划分。

然后,我想要做的是产生四个线程(每个子列表一个)。我遇到问题的部分是通过四个列表。我遇到了超出范围的问题,我不确定这里发生了什么:

        List<Thread> threads = new List<Thread>();

        for(int i = 0; i < t.Count; i++) 
        {
            threads.Add(new Thread(() => DoWork(t[i])));
        }

        foreach (Thread thread in threads)
        {
            thread.Start();
        }


        foreach (Thread thread in threads)
        {
            thread.Join();
        }
4

5 回答 5

8

这是一个经典,叫做捕获循环变量。

i在这段代码中,所有线程共享同一个变量。到线程运行时,主线程将产生i == t.Count,因此范围异常。

    for(int i = 0; i < t.Count; i++) 
    {
        threads.Add(new Thread(() => DoWork(t[i])));
    }

要解决这个问题:

    for(int i = 0; i < t.Count; i++) 
    {
        int copy = i;
        threads.Add(new Thread(() => DoWork(t[copy])));
    }
于 2012-10-12T20:23:27.997 回答
3

在这种情况下,捕获的变量不是您的朋友。尝试:

        for(int i = 0; i < t.Count; i++) 
        {
            int j=i;
            threads.Add(new Thread(() => DoWork(t[j])));
        }

发生的事情是,当您的原始文件运行完成时,i==t.Count. 而当 DoWork 执行时,你实际上是在做t[t.Count]. 不好!

于 2012-10-12T20:23:12.133 回答
0

您可以在启动线程时传入一个对象。因此,创建一个接收普通旧对象的 DoWork 重载。然后进行下面的修改。

    for(int i = 0; i < t.Count; i++) 
    {
        threads.Add(new Thread(DoWork));
    }

    foreach (Thread thread in threads)
    {
        thread.Start([your list right there]);
    }
于 2012-10-12T20:24:58.353 回答
0

正如 Henk Holterman 指出的那样,您正在关闭循环变量。我会做的是:

List<Thread> threads = t
    .Select(x => new Thread(() => DoWork(x)))
    .ToList();

另请参阅关闭被认为有害的循环变量

于 2012-10-12T20:41:47.853 回答
0

您可以将复杂类型发送到线程

public string FileSource;
public string FileName;
public ThreadPriority Priority;

public ThreadInfo(string File, ThreadPriority priority)
{
   FileSource = File;
   FileName = Path.GetFileNameWithoutExtension(File);
   Priority = priority;
}

static void Main()
{
   ThreadInfo ti = null;
   try
   {
      ti = new ThreadInfo(FileName, ThreadPriority.Normal);
      ThreadPool.QueueUserWorkItem(ThreadImageProcess.doWork, ti);
   }
   catch (Exception error)
   {
      MyEventLog.WriteEntry("Error creating Thread Pool: "+ error.StackTrace, EventLogEntryType.Error, (int)ImageProcess.MyEventID.ProcessFile, (int)ImageProcess.MyErrorCategory.Information, null);
   }
   finally
   {
      if (ti != null)
          ti = null;
   }
}

public static void doWork(object sender)
{
  string FileName = "";
  try
  {
     Thread.CurrentThread.Priority = (sender as ThreadInfo).Priority;
     FileName = (sender as ThreadInfo).FileSource;
  }
  catch (Exception error)
  {
     //send some message
  }
}
于 2012-10-12T20:42:14.607 回答