【问题标题】:race condition in async / await socket connection异步/等待套接字连接中的竞争条件
【发布时间】:2015-03-18 22:21:21
【问题描述】:

这个send方法会周期性的失败,因为控制权返回给调用线程,并且Send会在socket打开时再次被调用。

我正在使用 Stephen Toub 的课程:http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx

这会导致异常:在已连接的套接字上发出了连接请求。
确保异步方法不会遭受这种命运的正确方法是什么。基本上,在建立连接之前发送实际上无法完成。

    public async Task Send(byte[] tosend)
    {
        // this code is not correct / not thread safe.
        if (this.socket.Connected)
            await this.StartSending(tosend);
        else
            await StartConnecting(tosend);
    }

    private async Task StartSending(byte[] tosend)
    {
        var args = new SocketAsyncEventArgs();
        args.SetBuffer(tosend, 0, tosend.Length);
        var awaitable = new SocketAwaitable(args);
        await this.socket.SendAsync(awaitable);
    }

    private async Task StartConnecting(byte[] tosend)
    {
        var local = Dns.GetHostEntry(Dns.GetHostName());
        var ep = new IPEndPoint(local.AddressList.First(_ => _.AddressFamily == AddressFamily.InterNetwork), this.port);
        var args = new SocketAsyncEventArgs() { RemoteEndPoint = ep };
        var awaitable = new SocketAwaitable(args);
        await this.socket.ConnectAsync(awaitable);
        await StartSending(tosend);
    }

我的测试只是很快地连续调用Send

var sends = arrays.Select(_ => writer.Send(_)).ToArray();
Task.WaitAll(sends);

有时上面的代码可以正常工作,并且套接字已连接,socket.Connected 设置为 true 'on time'。其他时候,对this.socket.ConnectAsync 的调用不止一次。

【问题讨论】:

  • 你能澄清一下“控制权返回给调用线程吗?”
  • 当然,Send 被调用,它又调用 StartConnecting 和 ConnectAsync。然后调用 Send 的下一个调用,它再次调用 StartConnecting,因为 socket.Connected 仍然不正确。第一次连接成功,第二次抛出异常。例外是间歇性的。我必须运行几次测试才能重现问题,因为有时套接字会及时打开。我对 await 的理解是它返回控制而不阻塞——这就是我所指的。

标签: c# sockets async-await


【解决方案1】:

应该等待对Send 的调用,这样您就知道连接已建立,而且您知道您的数据正在按顺序发送。这最好由foreach 循环处理而不是.Select

foreach (var data in arrays)
    await writer.Send(data);

假设您创建了变量sends,以便您可以对其进行Task.WhenAll。不幸的是,由于您已经发现的原因,这不起作用。您可以使用名为Connect 的单独方法解决此问题,该方法异步连接到远程主机,但 发送数据。然后你可以这样做:

await writer.Connect();
var sends = arrays.Select(_ => writer.Send(_)).ToArray();

【讨论】:

  • 谢谢,我仍在尝试围绕异步模型进行思考......我认为指定任务依赖项应该更容易,这基本上是我在这里需要的。各种各样的延续。
  • 有点拗口,但这也可以: var send = writer .Send(arrays.First()) .ContinueWith(x => Task.WaitAll(arrays.Skip(1).Select( _ => writer.Send(_)).ToArray()),cancellationTokenSource.Token);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-22
  • 1970-01-01
  • 2010-11-27
  • 2017-08-16
  • 1970-01-01
  • 2021-06-04
  • 2015-08-13
相关资源
最近更新 更多