0

我有需要使用的第 3 方 WebSocket,请求以一种方法发送,响应以另一种方法给出。如何将这种类型的模式转换为更典型的异步/等待 TPL 任务,该任务将支持取消(通过令牌)、继续和所有其他好东西。

这是我到目前为止想出的,虽然我不确定它是否有效。我要到星期一才能测试它。

所以这是我的问题:

  1. 这行得通吗?

  2. 我一直在阅读关于TaskCompletionSource. 有没有更好的方法来做这一切TaskCompletionSource

  3. 我真的不喜欢锁,因为我知道它有可能会阻塞很长时间,但我不确定如何做得更好,因为如果我不锁定第二个调用,AsyncGetPositions可能已经清除任何位置回来。

  4. 即使lock我知道如果有超时或取消会产生问题,所以也许我只是删除取消令牌。我唯一能想到的另一件事是创建多个经过身份验证并准备好处理请求的客户端,然后像处理这些类型请求的线程池一样管理它们,但我不会很快这样做除此之外... idk。

    private object GetPositionsLock = new object();
    private IEnumerable<Position> Positions { get; } = new List<Position>();
    private Task PositionsReturned { get; set; }
    public async Task<List<Position>> AsyncGetPositions(CancellationToken token)
    {
        try
        {
            lock (GetPositionsLock)
            {
                Positions.Clear();
                IbWebSocket.reqPositions();
                PositionsReturned = new Task(null, token, TaskCreationOptions.None);
                PositionsReturned.GetAwaiter().GetResult();
                return token.IsCancellationRequested ? null : Positions.ToList().Where(x => x.Shares != 0).ToList();
            }
        }
        catch (TimeoutException ex)
        {
            //LogTimeout
            throw;
        }
        catch (Exception ex)
        {
            //LogError
            throw;
        }
    }
    
    ///  <summary>
    ///         Provides a position to the reqPositions() method.  When the last position has been received positionEnd() is called.
    ///  </summary>
    ///  <param name="contract"></param>
    ///  <param name="value"></param>
    ///  <param name="account"></param>
    public void position(string account, Contract contract, double value)
    {
        try
        {
            Positions.Concat(new Position(account, contract, value));
        }
        catch (Exception ex)
        {
            //LogError
            throw;
        }
    }
    
    /// <summary>
    ///     Indicates all the positions have been transmitted.
    /// </summary>
    public void positionEnd()
    {
        PositionsReturned = Task.CompletedTask;
    }
    
4

1 回答 1

2

这行得通吗?

不,您不应该使用Task构造函数lockasync代码一起使用或将阻塞与异步代码混合使用

我一直在阅读有关 TaskCompletionSource 的信息。有没有更好的方法可以用 TaskCompletionSource 来做这些?

是的,这就是用于此场景的类型。

我真的不喜欢锁,因为我知道它有可能会阻塞很长时间,但是我不确定如何做得更好,因为如果我不锁定对 AsyncGetPositions 的第二次调用可以清除任何位置已经回来了。

我建议让它工作,然后再处理重入的额外要求。这些中的每一个都已经足够难了。


你想要做的是有一个并在被调用TaskCompletionSource<T>时完成它。positionEnd为简单起见,开始时没有重入问题,也没有CancellationToken. 一旦你完全理解了TaskCompletionSource<T>,你就可以增加复杂性:

private List<Position> Positions { get; } = new();
private TaskCompletionSource<List<Position>> PositionsReturned { get; set; }
public Task<List<Position>> AsyncGetPositions()
{
  Positions.Clear();
  PositionsReturned = new();
  IbWebSocket.reqPositions();
  return PositionsReturned.Task;
}

public void position(string account, Contract contract, double value)
{
  Positions.Add(new Position(account, contract, value));
}

public void positionEnd()
{
  PositionsReturned.TrySetResult(Positions);
}
于 2021-03-20T22:32:45.897 回答