取消在Dispose和竞争条件下运行任务

时间:2017-12-12 21:40:31

标签: c# .net asynchronous

我在NamedPipeServerStreamNamedPipeClientStream周围构建了一个包装器。目前,Connect()上的Client方法如下所示:

    public async Task Connect(CancellationToken cancellationToken = default(CancellationToken))
    {
        var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _connectCancellationTokenSource.Token);

        await _pipe.ConnectAsync(cts.Token).ConfigureAwait(false);

        lock (_lock)
        {
            if (_pipe.IsConnected)
            {
                _connectCancellationTokenSource.Dispose();
                _connectCancellationTokenSource = null;
                _pipe.ReadMode = PipeTransmissionMode.Message;
                Connection.Set();
                Connected?.BeginInvoke(this, EventArgs.Empty, x => ((EventHandler)x.AsyncState).EndInvoke(x), Connected);

                BeginRead();
            }
        }
    }

和我的Dispose(bool)是这样的:

    protected override void Dispose(bool disposing)
    {
        lock (_lock)
        {
            _connectCancellationTokenSource?.Cancel();
            base.Dispose(disposing);
        }
    }

base.Dispose(bool)看起来像这样:

    protected virtual void Dispose(bool disposing)
    {
        if (!disposing) return;

        Connection.Reset();
        _pipe.Dispose();
        Connection.Dispose();

        _disposed = true;
    }

基本上,当连接任务仍然在运行并且等待它完成时,我无法调用Dispose(bool),因为它可能永远无法运行。

我的解决方法是创建一个链接的CancellationTokenSource并将其标记用作CancellationToken的{​​{1}},允许我自己在Task上取消它。

然而,这创造了一种竞争条件,其中客户端完全有可能连接并输入Dispose()仅在之后立即处置,导致它抛出if (_pipe.IsConnected)(也使用{ {1}}),因此BeginRead()_pipe上的lock

但是,我并不完全确定我的方法真的是那么理智,并且正在寻找一些验证或我可以改进的方法。我也担心我可能会在Connect() ...

中加入太多内容

0 个答案:

没有答案