websocket .NET中的Thread.Join()如何在线程终止之前返回?[关闭]

wtlkbnrh  于 2023-06-29  发布在  .NET
关注(0)|答案(1)|浏览(100)

**关闭。**此题需要debugging details。目前不接受答复。

编辑问题以包括desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem。这将帮助其他人回答这个问题。
6小时前关闭
截至1小时前,社区正在审查是否重新开放此问题。
Improve this question
我有一个进程,产生2个线程。一个线程从Web Socket读取,另一个线程写入同一个Web套接字。一旦所有东西都初始化并且Web Socket已经连接,主线程就会阻塞它在以下函数中创建的2个线程:

public void BlockUntilThreadsAreDone()
{
    if (_incomingThread.IsAlive)
        _incomingThread.Join();
    if (_outgoingThread.IsAlive)
        _outgoingThread.Join();
}

暂时一切正常。但几分钟后,我发现主线程将从这个函数返回。没有抛出任何异常,也没有看到任何异常日志记录。该函数只是因为Thread.Join()调用返回而返回。
我修改了函数,以等待两个线程在关闭时都会设置的事件,如下所示:

private readonly ManualResetEventSlim _messagingComplete = new(false);

public void BlockUntilThreadsAreDone()
{
    if (_incomingThread.IsAlive)
        _incomingThread.Join();
    Console.WriteLine($"Incoming thread state={_incomingThread.ThreadState}");

    if (_outgoingThread.IsAlive)
       _outgoingThread.Join();
    Console.WriteLine($"Outgoing thread state={_outgoingThread.ThreadState}");

    _messagingComplete.Wait(_cancellationToken);
}

线程对象是这样创建的

_incomingMessageThread = new(HandleIncomingMessages) { Name = "Incoming" };
_outgoingMessageThread = new(HandleOutgoingMessages) { Name = "Outgoing" };
private async void HandleIncomingMessages()
    try
    {
        Console.WriteLine("PapiClient HandleIncomingMessages starting");

        do
        {
            try
            {
                Message? message = await ReceiveIncomingMessageAsync();
                // Handle each message asynchronously so we can keep receiving more messages
                _ = Task.Run(
                    () =>
                    {
                        HandleIncomingMessage(message);
                    },
                    _cancellationToken
                );
            }
            catch (OperationCanceledException)
            {
                break;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception while handling messages: {ex}");
            }
        } while (!_cancellationToken.IsCancellationRequested && Connected);
    }
    catch (ObjectDisposedException) { }
    finally
    {
        _messagingComplete.Set();
        Console.WriteLine(
            $"PapiClient HandleIncomingMessages finishing: cancel={_cancellationToken.IsCancellationRequested}, connected={Connected}"
        );
    }
}

现在代码按照我的预期工作(即,函数只在我们完成处理后返回)。从技术上讲,这是不一样的,因为一旦第一个线程完成,它就会返回,但这对我的目的来说已经足够接近了。
有趣的是,我看到在进程启动几分钟后,两个线程最终都将记录其状态为ThreadState.Stopped。但是,线程肯定不会停止,因为我可以告诉进程继续通过Web Socket发送和接收数据,并且没有创建其他线程来使用Web套接字。此外,线程终止时发生的所有正常处理都不会在该点发生。
文档中说Join“阻塞调用线程,直到这个示例所代表的线程终止。”The documentation还说“线程永远不能离开Stopped状态。”
我很困惑。为什么Thread.Join()会返回,而一个线程在最肯定没有停止的情况下指示它的状态是Stopped
我怀疑这可能与网络和/或Web套接字有关。我运行了一个数据包捕获,在问题发生前几毫秒,WebSocket发送了一个未经请求的PONG消息。未经请求的PONG本身并不是问题,但这些PONG每30秒发送一次,因此在定时器唤醒并发送PONG消息后几毫秒内发生伪Thread.Join()似乎是不寻常的。
我在.NET core github issues list上找不到这个问题。有什么想法吗

hwamh0ep

hwamh0ep1#

看看你在Github上的代码库(顺便说一下,你的问题应该是独立的),两个函数HandleOutgoingMessagesHandleIncomingMessages被标记为async void。这意味着一旦它们到达第一个await,它们就会结束,并将继续发送到线程池。
您似乎期望它们所在的线程会以某种方式暂停,并在await继续发生时重新启动。事实并非如此。您的线程结束了,线程池将把它捡起来。
这本质上是线程和任务之间的混淆。您主要使用任务和异步,我建议您坚持使用,因此使用Task.Run来卸载Handle功能。
您需要进行以下更改

private readonly Task _incomingMessageThread;
private readonly Task _outgoingMessageThread;
private async Task HandleIncomingMessages()
{
private async Task HandleOutgoingMessages()
{

删除构造函数中以_incomingMessageThread =_outgoingMessageThread =开头的行。
_incomingMessageThread.Start(); _outgoingMessageThread.Start();行替换为

_incomingMessageThread = Task.Run(HandleIncomingMessages, _cancellationToken);
_outgoingMessageThread = Task.Run(HandleOutgoingMessages, _cancellationToken);

在需要等待线程完成的不同时刻,只需执行

await Task.WhenAll(_incomingMessageThread, _outgoingMessageThread);

您不需要检查它们是否已完成,在此之后它们将始终完成。
或者

try
{
    await Task
        .WhenAll(_incomingMessageThread, _outgoingMessageThread)
        .WaitAsync(TimeSpan.FromSeconds(DISCONNECT_TIMEOUT));
}
catch (OperationCanceledException)
{
    if (!_incomingMessageThread.IsCompleted)
        Console.WriteLine("Incoming task not stopping");
    if (!_outgoingMessageThread.IsCompleted)
        Console.WriteLine("Outgoing task not stopping");
}

相关问题