【问题标题】:Thread End checking from another Thread从另一个线程检查线程结束
【发布时间】:2011-08-10 15:24:59
【问题描述】:

我有一个通过 TcpClient 从端口读取数据的类。我创建了 2 个线程。

  1. 数据包读取线程
  2. 数据包处理线程

1 线程连续读取字节,一旦它得到一个正确的数据包,它就会将该数据包添加到队列中并启动第二个线程,然后从该队列中读取数据包并进行处理。

数据包处理应按顺序进行。因此,每次读取线程将数据包添加到队列中时,它都会检查该处理线程是否正在运行。如果不是,那么它会再次启动该线程。这是因为我们不希望多个数据包处理线程同时运行。

现在我的问题实际上与检查部分有关。我所做的是,我在课堂上添加了一个简单的 bool 变量。当我进入数据包处理线程时,我设置了 bool = true。当数据包读取线程检查这个布尔变量时,如果它是真的,那么它知道处理线程正在运行。当数据包处理线程到达结束时,它设置 bool = false。

这是正确的方法吗?还是这种方法容易出现竞争条件?

谢谢。

更新(一点细节):

delegate void PacketProcessDelegate();
PacketProcessDelegate PacketProcess = new PacketProcessDelegate(this.PacketProcessingThread);

PacketReadingThread() {
    Packet = GetPacket();  // thread blocks until packet is NOT received
    Queue.Synchronized(this.Queue).Enque(Packet);
    if (!this.IsProcessing)
        this.PacketProcess.BeginInvoke(null, null);
}

PacketProcessingThread() {
    this.IsProcessing=true;
    while(true) {
        Queue syncQueue;
        syncQueueu = Queue.Synchronized(this.Queue);
        if(syncQueueu.Count > 0)
            //packet extraction and processing code here
        else
            break;
    }
    this.IsProcessing=false;
}

目标:
数据包处理线程或者
*) 应该被终止并应该重新启动,但应该使用 ThreadPool 以防止每次创建新线程。这就是我使用 BeginInvoke 的原因。

*) 它应该以某种方式被阻止,直到没有收到另一个数据包。

【问题讨论】:

  • 有什么理由不能简单地使用Thread.IsAlive 属性?
  • 处理一个数据包后是否需要结束处理线程?如果没有,你可以继续运行处理线程,如果队列为空,它应该等待。
  • @drharris:实际上新线程是使用 BeginInvoke() 方法调用的。它实际上不是 Thread 变量。
  • BeginInvoke() 不会启动新线程。它调用调度程序或 UI 线程上的操作。
  • 实际上,忘记了Delegate.BeginInvoke(),它确实启动了一个线程池线程。不过,如果您需要跟踪线程生命周期,这可能不是最佳实践。在这种情况下最好使用Thread,并考虑使用BlockingCollection<T>,正如乔恩和我自己所建议的那样。

标签: .net multithreading


【解决方案1】:

你肯定有一个竞争条件,没有比描述的更多的锁定。如果两个数据包快速连续进入,您可能会看到标志两次为假并启动两个不同的线程。您可以通过使 reading 线程在启动线程时将标志设置为 false 来避免这种情况 - 因此标志意味着“线程正在运行或正在启动”。

尚不清楚为什么您仍然需要继续启动线程 - 为什么不让 一个 处理线程在队列为空时阻塞等待新数据包?如果您使用的是 .NET 4,则使用 BlockingCollection<T> 非常容易。

编辑:如果您不需要按特定顺序处理数据包,您可以为每个数据包启动一个新的线程池工作项:

ThreadPool.QueueUserWorkItem(ProcessPacket, packet);

...

private void ProcessPacket(object state)
{
    Packet packet = (Packet) state;
    ...
}

【讨论】:

  • 我还没有使用 BlockingCollection 并试图让解决方案在 .net 2.0 上运行。实际上数据包处理线程处理数据包非常快。所以我希望这个线程要么在完成时终止,要么应该被阻止,直到队列中有新的数据包可用。但是不知道怎么用这种方式阻塞线程。
  • @sallushan:好吧,您可以相当容易地编写自己的生产者消费者队列。我在 前(在 C# 2 之前)在yoda.arachsys.com/csharp/threads/deadlocks.shtml 编写了一个简单的实现 - 搜索“生产者消费者队列 .net”应该会找到很多示例。我建议每次都启动一个新线程。当然,另一种选择是使用线程池。
【解决方案2】:

您可能希望为此使用BlockingCollection<T> 类。您的处理线程将阻塞等待下一个项目入队。

一些建议阅读:Threading in C#

【讨论】:

    【解决方案3】:

    另一种设计是改用wait handle 并等到它发出信号后再调用处理 - 在 CPU 方面可能更好。

    【讨论】:

    【解决方案4】:

    首先,您应该避免使用“共享资源”,这意味着数据、属性、.. 在多个线程上使用。

    如果你使用它们,你应该同步它们(s.ReaderWriterLockSlim

    还有一些名为 TPL 的扩展类可以更轻松地使用线程。请参阅link 的示例。

    如果您使用 TPL 和 Task Class,您可以使用 Wait Method 来完成任务。

    -> 任务 myTask = Task.Factory.StartNew(() => Foo());

            myTask.Wait();
    

    当我做多线程的事情时,我总是推荐使用 TPL 或至少使用 Threadpool。

    你也可以考虑使用RX,它在线程和linq上有某种统一的编程模型

    【讨论】:

      猜你喜欢
      • 2022-08-06
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多