我正在运行以下代码来启动我的线程,但它们没有按预期启动。出于某种原因,一些线程以相同的对象开始(有些甚至没有启动)。如果我尝试调试,它们会很好地开始(我点击F10来逐步执行代码时添加了额外的延迟)。
这些是我的表单应用程序中的函数:
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var t = new Thread(() => WorkerThread(tmpItem, i));
t.Start();
//return t;
}
}
private void WorkerThread(ConfigurationItem cfgItem, int mul)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10*mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
任何人都可以帮助我吗?
答案 0 :(得分:2)
Starting线程并没有真正启动线程。相反,它会将其安排执行。即在某些时候,它会在安排时运行。调度线程是一个复杂的主题和操作系统的实现细节,因此您的代码不应该期望某个调度。
你也在你的lambda中捕获变量。有关与此相关的问题,请参阅this post(有一个关于捕获变量的部分)。
答案 1 :(得分:2)
你只是遇到了(被我称之为)lambda错误。
您直接从foreach循环中提供ConfigurationItem
。这导致了所有线程获得相同项目(最后一个)的事实。
要使其工作,您必须为每个项目创建一个引用并将其应用于每个线程:
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var currentI = i;
var currentItem = tmpItem;
var t = new Thread(() => WorkerThread(currentItem, currentI));
t.Start();
//return t;
}
你还应该考虑使用ThreadPool。
答案 2 :(得分:1)
问题似乎在那里:() => WorkerThread(tmpItem, i)
我不习惯Func<>
,但它似乎像.NET 2.0中的匿名代理一样工作。因此,您可能会引用WorkerThread()
方法的参数。因此,它们的值将在以后检索(当线程实际运行时)。
在这种情况下,您可能已经处于主线程的下一次迭代......
请改为尝试:
var t = new Thread(new ParametrizedThreadStart(WorkerThread));
t.Start(new { ConfigurationItem = tmpItem, Index = i } );
[编辑]其他实施。如果您将来需要将新参数传递给线程,则更灵活。
private void startWorkerThreads()
{
int numThreads = config.getAllItems().Count;
int i = 0;
foreach (ConfigurationItem tmpItem in config.getAllItems())
{
i++;
var wt = new WorkerThread(tmpItem, i);
wt.Start();
//return t;
}
}
private class WorkerThread
{
private ConfigurationItem _cfgItem;
private int _mul;
private Thread _thread;
public WorkerThread(ConfigurationItem cfgItem, int mul) {
_cfgItem = cfgItem;
_mul = mul;
}
public void Start()
{
_thread = new Thread(Run);
_thread.Start();
}
private void Run()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10 * _mul);
}
this.Invoke((ThreadStart)delegate()
{
this.textBox1.Text += "Thread " + _cfgItem.name + " Complete!\r\n";
this.textBox1.SelectionStart = textBox1.Text.Length;
this.textBox1.ScrollToCaret();
});
}
}
答案 3 :(得分:0)
你真的需要手动生成线程(这是一项相当昂贵的任务)吗?您可以尝试切换到ThreadPool。
答案 4 :(得分:0)
你不能假设线程将以它们被调用的相同顺序运行,除非你强制它,并导致它们之间的依赖。
所以真正的问题是 - 你的目标是什么?
答案 5 :(得分:0)
我认为错误在其他地方。以下是一些可以帮助您调试的提示:
为每个线程指定一个名称,并显示线程名称而不是配置项名称:
this.textBox1.Text + =“Thread”+ Thread.Current.Name +“Complete!\ r \ n”;
显示config.getAllItems()的内容,可能是某些项目具有相同的名称(重复)
===========
以下是有关使用winforms进行多线程处理的一些其他信息:
不要直接创建新的Thread,而是使用ThreadPool:
ThreadPool.QueueUserWorkItem(state =&gt; {WorkerThread(tmpItem,i);});
我希望这会对你有所帮助。
答案 6 :(得分:0)
感谢大家!
我刚刚实现了线程池,它就像一个魅力 - 还有额外的好处,就是不会同时产生过多的线程。
我也会看看其他解决方案,但这次绕线程池将使我不必手动检查有太多配置的bozos;)