在过去的几个小时里,我一直在用这个打击我的头,所以这里就是这样。对于那些对多线程经验不足的人来说,这可能是一个常见错误吗?谁知道呢。
在包含的代码中,我实例化了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());
}
答案 0 :(得分:1)
Thread.Start并不意味着线程立即开始运行,它导致操作系统将当前实例的状态更改为ThreadState.Running。一旦线程处于ThreadState.Running状态,操作系统就可以将其安排执行,但它并不意味着首先创建的线程将首先被执行。这就是问题的原因。
如果要使3线程按顺序运行,则应查看线程同步。
答案 1 :(得分:1)
正如其他人已经指出的那样,Console.WriteLine可能比变量的增加慢。解决此问题的另一种方法是使用线程局部变量。它们不应受其他线程更改的影响。对于C#,我找到了这个链接:http://msdn.microsoft.com/en-us/library/dd642243(v=vs.110).aspx
这种方法的优点是,您不必维护与线程一样多的变量,并且可以同时完成线程的工作。
祝你好运!答案 2 :(得分:0)
DateNow
的同时,其他线程读取相同的值。
考虑一下:
其中一个条件为true - 因此创建了一个新线程,但是当该线程将被安排运行时,您无法控制。
所以同时 - 你的主线程改变了DateNow
...所以..当新创建的线程实际运行时 - 他看到并打印的值 - 与通过条件的“sane”值不同...
考虑一下这个更奇怪的结论: 当写入32位或更少的变量时,C#提供atomicy
Atomicy意味着(以非常一般和不准确的方式)操作不能在中间中断... 所有线程获得一些CPU,然后操作系统停止它,并安排另一个线程运行..在那之后的一段时间 - 操作系统将再次安排我们的线程,它将从它停止的地方继续。 原子操作不能在中间停止......它要么尚未开始,要么已经完成。
但DateTime实际上是64位..这意味着操作系统可以在写入新值时中断主线程。这意味着直到主线程再次被调度 - DateNow
将有一些奇怪的,不一致的值 - 并且任何其他线程都可以同时读取该值。
答案 3 :(得分:0)
由于您正在为线程函数使用lambda表达式,因此直到线程执行开始后的某个时间才会复制DateNow的值。由于线程之间没有同步,这是完全不可预测的。在您开始创建第二个线程然后显示DateNow的当前值(4:35)之前,第一个线程(Period 0)可能不会获得任何cpu。第1期会发生同样的情况,然后在添加1分钟后再次绕过循环时,第2期最终会运行。切换每个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的当前值的副本现在发生在主执行线程中的可预测位置。但是,由于无法保证三个线程将按创建顺序运行,因此订单可能仍然无法正确显示。