异步套接字和AutoResetEvent

时间:2017-12-07 06:10:02

标签: c# sockets asynchronous events

我有一个应用程序,其中主应用程序创建一个循环并等待方法返回一些数据。 即

Main.cs
var obj = new MySuperClass();
MyData data; 
while (obj.ReadData(out data)) {
  // do something with data
}

数据本身来自异步套接字连接,因此我使用AutoResetEvent来指示数据何时准备就绪,因此主应用程序可以使用它。

class MySuperClass {
  private MyData _dataFromSocket;
  private AutoResetEvent _ev = new AutoResetEvent(false);
  public bool ReadData(out MyData data) {
    _autoResetEvent.WaitOne();
    data = _dataFromSocket;
    // here more logic to return false, but that's not the case or the issue
    return true;  

  }

  /// async socket callback and data parse
  private void MyAsyncCallback(byte rawData) {
    while (canParse(rawData, out _dataFromSocket)) { // somehow parse the raw bytes 
      //if we could parse, signal that we have a packet
      _ev.Set(); // set the signal
    }
  }
}

设置_ev后,Read返回,主应用获取数据,每个人都很高兴。 但在某些情况下,当使用大缓冲区调用AsyncCallback时我可以反序列化多个数据包,即5,主应用程序将只收到2或3,就像事件跳过重置和Read方法不立即返回。

知道怎么解决这个问题吗?是否有更快的事件实现可以更快地发出信号。

我无法改变应用程序的体系结构,这些是需求,它必须在主线程中工作,数据如何不关心,在这种情况下,我必须使用异步套接字。

感谢任何想法。

1 个答案:

答案 0 :(得分:1)

我建议使用同步队列(FIFO)。您可以在MyAsyncCallback中将结果分配给全局变量,并在将来的任何时间在ReadData中读取它,而不进行任何同步。这不是最理想的,也无法发挥作用。

简化改进:

class MySuperClass
{
  private readonly object _syncRoot  = new object();
  private Queue<MyData>   _dataQueue = new Queue<MyData>();
  private AutoResetEvent  _ev        = new AutoResetEvent(false);

  public bool ReadData(out List<MyData> dataList)
  {
    dataList = new List<MyData>();

    _autoResetEvent.WaitOne();

    lock(_syncRoot) // lock access to queue for other threads
    {
      while(_dataQueue.Count > 0)
      {
        dataList.Add(_dataQueue.Dequeue());
      }
    }        

    return true;  
  }

  /// async socket callback and data parse
  private void MyAsyncCallback(byte rawData)
  {
    MyData data;

    while (canParse(rawData, out data)) 
    { // somehow parse the raw bytes 
      //if we could parse, signal that we have a packet

      lock(_syncRoot) // lock access to queue for other threads
      {
        _dataQueue.Enqueue(data);
      }
    }
    _ev.Set(); // set the signal after all packets parsed, depends on your requirements, maybe after each?
  }
}