首页 > 解决方案 > VB.net 多线程问题 - 线程长时间休眠

问题描述

我有一个用 VB.net 编写的旧版 winform 应用程序,它有几个线程(3-4 个线程),每个线程在应用程序处于活动状态时循环运行。每个线程循环都会遍历一个项目列表并检查它们的状态。例如,一个线程有一个与数据库相关的项目列表,它会检查它们是否发生了变化并采取相应的行动。另一个线程有一个需要从另一个 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 毫秒)。

我不明白为什么有时一个特定的线程会休眠/挂起很长时间,而其他线程循环执行多次。

标签: vb.netmultithreading

解决方案


线程让 CPU 运行的时间完全由平台 (OS) 管理。您无法预测任意线程何时唤醒。当任意线程获得 CPU 时,由 .NET 框架和操作系统线程调度程序决定。线程是允许以并行方式运行任务的机制,但线程本身在获得 CPU 时间时并不能为您提供任何保证。

如果您需要某种运行线程的顺序,您可以通过某种线程同步机制(例如class ManualResetEventor class AutoResetEvent)来实现,但在您的情况下,这实际上可能是一种反模式。创建线程是为了让您的代码以并行方式运行,如果没有充分的理由要同步线程(如生产者消费者模式),则不应这样做。

顺便说一句 - 在大多数情况下,让线程在没有任何等待的情况下运行,就像 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循环。

在某些情况下,这种“随机等待”是不可避免的——(例如,如果 3rd 方 API 没有发出异步事件到达的信号),但在大多数情况下,这样的代码是代码设计不当的标志。


推荐阅读