线程启动期间的竞争条件?

发布于 2024-08-25 18:29:20 字数 842 浏览 4 评论 0原文

我正在运行以下代码来启动我的线程,但它们没有按预期启动。由于某种原因,某些线程以相同的对象启动(有些甚至不启动)。如果我尝试调试,它们会正常启动(我单击 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

栩栩如生 2024-09-01 18:29:20

启动线程并未真正启动线程。相反,它会安排它执行。即在某个时刻它将按计划运行。调度线程是一个复杂的主题,也是操作系统的实现细节,因此您的代码不应期望进行特定的调度。

您还在 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.

一笑百媚生 2024-09-01 18:29:20

你刚刚遇到了(我称之为)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;
}

并且您还应该考虑使用线程池。

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:

foreach (ConfigurationItem tmpItem in config.getAllItems())
{
        i++;
        var currentI = i;
        var currentItem = tmpItem;
        var t = new Thread(() => WorkerThread(currentItem, currentI));
        t.Start();
        //return t;
}

And you should also consider using a ThreadPool.

氛圍 2024-09-01 18:29:20

问题似乎就在那里: () => 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();
        });
    }
}

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 the WorkerThread() 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 :

var t = new Thread(new ParametrizedThreadStart(WorkerThread));
t.Start(new { ConfigurationItem = tmpItem, Index = i } );

[EDIT] Other implementation. More flexible if you need to pass new parameters to the thread in the future.

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();
        });
    }
}
旧时浪漫 2024-09-01 18:29:20

您真的需要手动生成线程(这是一项相当昂贵的任务)吗?您可以尝试切换到线程池。

Do you really need to spawn threads manually (which is a rather expensive task) ? You could try to switch to the ThreadPool instead.

要走就滚别墨迹 2024-09-01 18:29:20

您不能假设线程将按照它们被调用的顺序运行,除非您强制它,并导致它们之间的依赖关系。

所以真正的问题是——你的目标是什么?

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 ?

心在旅行 2024-09-01 18:29:20

我认为错误在其他地方。以下是一些帮助您调试的提示:

  1. 为每个线程指定一个包含以下内容的名称,并显示线程名称而不是配置项名称:

    this.textBox1.Text += "Thread " + Thread.Current.Name + " Complete!\r\n";

  2. 显示config.getAllItems()的内容,可能有些项同名(重复)

===========

以下是有关 winforms 多线程的一些附加信息:

  1. 不要直接创建新线程,而是使用线程池:

    ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });

  2. 如果您确实想创建线程,请使用 this.BeginInvoke 而不是 this.Invoke 您的工作线程将更快完成=>更少的并发线程=>更好的全局性能
  3. 不要在循环中调用 Thread.Sleep,只需进行一次大睡眠:Thread.Sleep(10*mul*100);

我希望这会对您有所帮助。

I think that the error is somewhere else. Here are some hints to help you debug :

  1. 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";

  2. 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:

  1. dont create new Thread directly, use the ThreadPool instead :

    ThreadPool.QueueUserWorkItem(state => { WorkerThread(tmpItem, i); });

  2. If you really want to creat your threads, use this.BeginInvoke instead of this.Invoke your worker thread will finish sooner => less concurrent thread => better global performance
  3. don't call Thread.Sleep in a loop, just do a big sleep: Thread.Sleep(10*mul*100);

I hope that this will help you.

无需解释 2024-09-01 18:29:20

谢谢大家!

我刚刚实现了线程池,这就像一个魅力——还有一个额外的好处,就是不会一次生成太多线程。

我也会看看其他解决方案,但这一次线程池将使我不必手动检查配置过多的 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 ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文