启动任务,不要等待并仍然获取实例

Start Task, don't await and still get the instance

我有一个特别的问题,我的任务开始(第 13 行)从来没有 returns。但我不想等待这个。 (为了进行适当的清理,我需要参考)

我的代码在这里:https://pastebin.com/4Zb1bzJX

有人说这个调用应该 return,其他人说使用“ConfiguraAwait”(没有选项,因为我将无法获得实例)

我不确定如何以“正确的方式”进行操作。使用 _bufferWorker = Task.Factory.StartNew(()=> BufferWorker()); 让它工作会很容易,但这感觉很老套而且不对,因为只是创建一个委托来调用已经是 Task 的东西似乎是错误的)

我该如何解决这个问题? (也许任务甚至不是我想去这里的方式,只是使用旧的线程?)

包含完整代码以使其更具可读性:

    private async Task BufferWorker()
    {
        while (_working)
        {
            if(!_queue.TryDequeue(out var data)) continue;
            await Task.Delay(_provider.BufferedDuration);
            _provider.AddSamples(data, 0, data.Length);
        }
    }

    public void Open()
    {
        _working = true;
        _bufferWorker = BufferWorker();//this line never returns
    }
    public void Close()
    {
        _working = false;
        _bufferWorker.Wait();
        _bufferWorker = null;
    }

编辑:我在从来没有 returns

的行上添加了评论

重要信息:我还添加了我删除的一行=>如果 TryQequeue 失败我继续循环。这很重要,因为如果我点击第一个等待,任务将 return。有了这个我可以解决(只需等待 1 毫秒),但这也是一个 hack。

如果您在没有 await 的情况下调用 async 方法,它将隐式等待结果。由于任务永远不会结束,它将永远等待。您可以使用 Factory.StartNew 方法来解决这个问题,也可以改用线程。对于 运行 长时间的作业,尤其是那些有自己的 while(_working) 循环的作业,线程通常是比任务更好的选择。

private void BufferWorker()
    {
        while (_working)
        {
            if(!_queue.TryDequeue(out var data)) continue;
            Thread.Sleep(_provider.BufferedDuration);
            _provider.AddSamples(data, 0, data.Length);
        }
    }

    public void Open()
    {
        _working = true;
        _bufferWorker = new Thread(BufferWorker);
        _bufferWorker.Start();

    }
    public void Close()
    {
        _working = false;
        _bufferWorker.Join();
        _bufferWorker = null;
    }

请注意,您仍然需要在 continue 之前插入一些 sleep,否则会导致忙等待。

TryDequeue 当队列中没有项目时立即 returns null + Thread.Sleep(FixedTime) 将使 BufferWorker 工作有一些延迟。最好使用Windows/.Net内置的同步原语(Monitor/Event)或者数据结构让BufferWorker瞬间工作:

BlockingCollection<TData> _queue = new BlockingCollection<TData>(10);
private void BufferWorker(CancellationToken ct)
{
    try {
        foreach (var data in _queue.GetConsumingEnumerable(ct))
            _provider.AddSamples(data, 0, data.Length);
    }
    catch (OperationCanceledException) { } // exit requested
}

CancellationTokenSource _cts;
public void Open()
{
    _cts = new CancellationTokenSource();
    new Thread(x => BufferWorker(_cts.Token)).Start();
}
public void Close()
{
    _cts.Cancel();
}