运行多个并发线程的后果

本文关键字:后果 线程 并发 运行 | 更新日期: 2023-09-27 18:02:32

在过去的几个小时里,我一直在思考这个问题,所以现在开始吧。也许对于没有多线程经验的人来说,这是一个常见的错误?谁知道呢。

在包含的代码中,我实例化了3个运行方法DisplayValues(DateTime Now, int Period)的线程。调试器在每个if语句中停止三次,对于每个语句,它都使用正确的值执行方法调用。问题是Console.WriteLine显示不稳定的值,与调用的方式完全不同。

控制台调用DisplayValues() 3次,参数如下,正确:DisplayValues('{5/8/2014 4:20:00 AM}', 0);DisplayValues('{5/8/2014 4:35:00 AM}', 1);DisplayValues('{5/8/2014 4:50:00 AM}', 2);

但是输出是完全不同的:

5/8/2014 4:35:00 AM周期:0

5/8/2014 4:50:00 AM周期:1

5/8/2014 4:51:00 AM周期:2

调试器确认这一点。因为它是一个控制台应用程序,我认为它可能是所有的方法都是静态的,所以我把DisplayValues()移动到一个类。然后我认为所有三个类实例都具有相同的名称,因此我更改了名称。然后我认为它可能是CancellationTokenSource对象,所以我也删除了它。

不用说,没有线程,输出是正确的。

我知道有一个明显的原因,我只是不知道是什么。

任何帮助都是感激的。谢谢。

bool thread0Running = false;
bool thread1Running = false;
bool thread2Running = false;
DateTime DateNow = new DateTime(2014, 5, 8, 4, 0, 0);
while ((!thread0Running || !thread1Running || !thread2Running) && DateNow.Hour == 4)
{
    if ((DateNow.Hour == TaskDateTime.Hour) && (DateNow.Minute == 20))
    {
        thread0Running = true;
        Class myClass0 = new Class();
        new Thread(() => myClass0.DisplayValues(DateNow, 0, cts0.Token)).Start();
    }
    else if ((DateNow.Hour == TaskDateTime.Hour) && (DateNow.Minute == 35))
    {
        thread1Running = true;
        Class myClass1 = new Class();
        new Thread(() => myClass1.DisplayValues(DateNow, 1, cts1.Token)).Start(); 
    }
    else if ((DateNow.Hour == TaskDateTime.Hour) && (DateNow.Minute == 50))
    {
        thread2Running = true;
        Class myClass2 = new Class();
        new Thread(() => myClass2.DisplayValues(DateNow, 2, cts2.Token)).Start();
    }
    DateNow = DateNow.AddMinutes(1);
}
public void DisplayValues(DateTime Now, int Period, Object obj)
{
        Console.WriteLine(Now.ToString() + " Period: " + Period.ToString());
}

运行多个并发线程的后果

线程。Start并不意味着线程立即开始运行,它导致操作系统将当前实例的状态更改为ThreadState.Running。一旦线程处于ThreadState。运行状态时,操作系统可以安排它执行,但这并不意味着首先创建的线程将首先执行。这就是问题的原因。

如果您想让这3个线程按顺序运行,您应该查看线程同步。

正如其他人已经指出的那样,控制台。WriteLine可能比变量的增长慢。解决这个问题的另一种方法是使用线程局部变量。它们不应该受到其他线程更改的影响。对于c#,我找到了这个链接:http://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx

这种方法的优点是,您不必像线程那样维护那么多的变量,并且线程的工作可以同时完成。

祝你好运!

我认为原因是主线程在同一时间改变DateNow,其他线程读取相同的值。

考虑一下:其中一个条件为真——创建了一个新线程,但是您无法控制该线程何时计划运行。所以在此期间-你的主线程改变了DateNow…所以. .当新创建的线程实际运行时,它看到并打印的值与"正常"值不同,该值通过了条件…

想想这个更奇怪的情况:c#在写入32位或更小的变量时提供原子性

原子性意味着(以一种非常一般和不准确的方式)操作不能在中间被中断…所有线程都得到一些CPU,然后操作系统停止它,并安排另一个线程运行。在那之后的一段时间,操作系统会再次调度我们的线程,它会从它停止的地方继续。原子操作不能中途停止。它要么还没有开始,要么已经完成了。

但是DateTime实际上是64位…这意味着操作系统可以中断主线程——在写入新值的过程中。这意味着,直到主线程再次调度- DateNow将有一些奇怪的,不一致的值-任何其他线程可以在此期间读取该值。

由于您正在为线程函数使用lambda表达式,因此直到线程执行开始后的某个时间才复制DateNow的值。由于线程之间没有同步,所以这是完全不可预测的。第一个线程(周期0)可能没有得到任何cpu,直到您开始创建第二个线程,然后显示DateNow的当前值(4:35)。周期1也是一样,然后周期2最终开始运行一旦你在1分钟后再次进入循环。将每个lambda切换为使用自己的变量,如下所示:

if ((DateNow.Hour == TaskDateTime.Hour) && (DateNow.Minute == 20))
{
    DateTime DateNow0 = DateNow; // DAteTime is a struct so this is a value copy
    thread0Running = true;
    Class myClass0 = new Class();
    new Thread(() => myClass0.DisplayValues(DateNow0, 0, cts0.Token)).Start();
}

使用DateNow1和DateNow2的其他2块。有了这个改变,我想你就会得到你想要的输出。DateNow当前值的复制现在发生在主执行线程中可预测的位置。但是,顺序可能仍然不能正确输出,因为不能保证这三个线程将按照创建的顺序运行。