线程启动期间的竞争条件?
我正在运行以下代码来启动我的线程,但它们没有按预期启动。由于某种原因,某些线程以相同的对象启动(有些甚至不启动)。如果我尝试调试,它们会正常启动(我单击 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();
});
}
有人能帮助我吗?
I'm running the following code to start my threads, but they don't start as intended. For some reason, some of the threads start with the same objects (and some don't even start). If I try to debug, they start just fine (extra delay added by me clicking F10 to step through the code).
These are the functions in my forms app:
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();
});
}
Anyone able to help me out?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
启动线程并未真正启动线程。相反,它会安排它执行。即在某个时刻它将按计划运行。调度线程是一个复杂的主题,也是操作系统的实现细节,因此您的代码不应期望进行特定的调度。
您还在 lambda 中捕获变量。请参阅这篇文章(有一个关于捕获变量的部分)与这样做相关的问题。
Starting a thread doesn't really start the thread. Instead it schedules it for execution. I.e. at some point it will get to run when it is scheduled. Scheduling threads is a complex topic and an implementation detail of the OS, so your code should not expect a certain scheduling.
You're also capturing variables in your lambda. Please see this post (there is a section on Captured Variables) for the problems associated with doing that.
你刚刚遇到了(我称之为)lambda 错误。
您可以直接从 foreach 循环中提供
ConfigurationItem
。这导致了这样一个事实:所有线程都获得相同的项目(最后一个)。要使其正常工作,您必须为每个项目创建一个引用并将其应用于每个线程:
并且您还应该考虑使用线程池。
You just run into the (be me called) lambda error.
You provide the
ConfigurationItem
from the foreach loop directly. This leads to the fact, that all your threads get the same item (the last one).To get this to work you have to create a reference for each item and apply this to each thread:
And you should also consider using a ThreadPool.
问题似乎就在那里:
() => WorkerThread(tmpItem, i)
我不习惯
Func
但它似乎像 .NET 2.0 中的匿名委托一样工作。因此,您可能会引用WorkerThread()
方法的参数。因此,稍后(当线程实际运行时)检索它们的值。在这种情况下,您可能已经处于主线程的下一个迭代中...
请尝试以下操作:
[编辑]其他实现。如果将来需要向线程传递新参数,则更加灵活。
The problem seems to be there :
() => WorkerThread(tmpItem, i)
I'm not used to
Func<>
but it seems to work like anonymous delegates in .NET 2.0. Thus, you may have a reference to the arguments of theWorkerThread()
method. Hence, their values are retrieved later (when the thread actually runs).In this case, you may already be at the next iteration of your main thread...
Try this instead :
[EDIT] Other implementation. More flexible if you need to pass new parameters to the thread in the future.
您真的需要手动生成线程(这是一项相当昂贵的任务)吗?您可以尝试切换到线程池。
Do you really need to spawn threads manually (which is a rather expensive task) ? You could try to switch to the ThreadPool instead.
您不能假设线程将按照它们被调用的顺序运行,除非您强制它,并导致它们之间的依赖关系。
所以真正的问题是——你的目标是什么?
You can't assume that the threads will run in the same order they were called, unless you force it, and cause a dependency between them.
So the real question is - what is your goal ?
我认为错误在其他地方。以下是一些帮助您调试的提示:
为每个线程指定一个包含以下内容的名称,并显示线程名称而不是配置项名称:
this.textBox1.Text += "Thread " + Thread.Current.Name + " Complete!\r\n";
显示config.getAllItems()的内容,可能有些项同名(重复)
===========
以下是有关 winforms 多线程的一些附加信息:
不要直接创建新线程,而是使用线程池:
ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });
我希望这会对您有所帮助。
I think that the error is somewhere else. Here are some hints to help you debug :
Give a name containing to each thread, and display the thread name instead of the config item name :
this.textBox1.Text += "Thread " + Thread.Current.Name + " Complete!\r\n";
Display the content of config.getAllItems(), may be that some items has the same name (duplicated)
===========
Here are some additional information about multi threading with winforms:
dont create new Thread directly, use the ThreadPool instead :
ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });
I hope that this will help you.
谢谢大家!
我刚刚实现了线程池,这就像一个魅力——还有一个额外的好处,就是不会一次生成太多线程。
我也会看看其他解决方案,但这一次线程池将使我不必手动检查配置过多的 bozos;)
Thanks to all of you!
I just implemented the threadpool, and that worked like a charm - with the added bonus of not spawning too many threads at once.
I'll have a look at the other solutions, too, but this time around the threadpool will save me from having to manually check for bozos with too many configs ;)