我有一个用VB.net编写的旧版winform应用程序,该应用程序有多个线程(3-4个线程),每个线程在应用程序处于活动状态时循环运行。
每个线程周期都会遍历项目列表并检查其状态。
例如,一个线程具有与DB相关的项目列表,它检查它们是否更改并采取相应措施。
另一个线程具有需要从另一个TCP服务器更新的项目列表。
每个线程完成其列表后,便具有Sleep(300)。
我有一个奇怪的现象,其中一个线程有时会启动,然后我看到其他线程多次运行其循环,只有第一个线程才能继续。这导致该线程的循环需要很长时间。
每个线程的循环看起来像这样:
Do While locParent.ServiceState = IThreadMain.eServiceState.Run
ReadItems() ' go through the list
Threading.Thread.Sleep(300) ' sleep
Application.DoEvents()
Loop
在ReadItems()代码中,还有短暂的睡眠调用(最长1000毫秒)。
我不明白为什么有时一个特定的线程睡眠/暂停很长一段时间,而另一个线程循环执行多次。
答案 0 :(得分:1)
平台(OS)完全管理线程使CPU运行的时间。您无法预测任意线程何时醒来。当任意线程获取CPU时,它由.NET框架和操作系统线程调度程序决定。线程是允许并行运行任务的机制,但是线程本身在获得CPU时间时并不能提供任何保证。
如果您需要某种顺序的线程运行顺序,则可以通过某种线程同步机制(例如class ManualResetEvent
或class AutoResetEvent
)来实现,但是在您的情况下,这可能是实际上是一种反模式。创建线程是为了允许您的代码以并行方式运行,并且如果没有充分的理由要同步线程(例如生产者使用者模式),则不应该这样做。
BTW-在大多数情况下,让线程在没有诸如Thread.Sleep(100)之类的人造睡眠的情况下循环运行而无需任何等待,反模式会损害系统的整体性能。仅当线程有有意义的工作要做时,才应运行线程。例如,如果线程处理来自TCP套接字的某些数据,则它应无限期休眠,直到来自套接字的某些数据到达为止。这样的代码应该以这种方式示意性地编写。
AutoResetEvent Event; // Shared variable.
ConcurrentQueue<SomeDTO> Queue; // Shared thread safe queue.
// THREAD 1.
while(true)
{
// Waits indefinitely.
Event.WaitOne();
// Read data from thread safe queue.
SomeDTO var;
if (Queue.TryDequeue(out var)==true)
{
// Process data.
}
}
// THREAD 2 - if new data arrive from TCP.
SomeDTO var=ReadDataFromTCP();
Queue.Enqueue(var);
Event.Set(); // This wakes up THREAD 1.
此算法有一些替代方法,例如,如果某些处理要等到一段时间才能完成,并且向Event.WaitOne()
添加一些超时,并且在这种情况下需要进行一些处理(发送错误消息),但是通常线程不应仅仅为了防止线程旋转而“随机等待” 100ms或500ms。它们应该等待直到其他线程将其唤醒,因为它们有一些数据需要处理或有意义的超时到期。
如果唤醒线程只是为了进行检查,就没有什么要处理的,这种情况可以通过其他方式(例如,如果我们知道何时来自TCP套接字的数据到达)来确定,那么这种“随机等待”就是浪费CPU周期数。
在某些情况下,这种“随机等待”是不可避免的(例如,如果第三方API不能表示异步事件到达),但是在大多数情况下,此类代码是代码设计不当的标志。