Queue调用套接字的异步方法BeginSend

时间:2014-05-04 12:02:27

标签: c# multithreading sockets asynchronous

我需要将 BeginSend 调用排队到套接字,我需要按时间顺序执行它们。为此,我使用信号量来表示回调函数何时清除 大部分时间它都有效,因为每个异步回调都是在一个独立的线程上执行的,但偶尔会在新的异步调用中使用当前回调中使用的相同线程。当发生这种情况时,该线程被锁定,等待释放信号量,但由于应该清除信号量的同一线程正在等待它被清除,因此该线程将被永久锁定。

为了说明问题,这里有一个测试代码:

static Semaphore semaphore = new Semaphore(1, 1);
static IList<byte[]> buffer = new List<byte[]>();
static void Main()
{
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 1, 8 }), 123));
    while (true) // data feed
    {
        lock (buffer)
        {
            buffer.Add(new byte[1460]);
            if (buffer.Count == 1)
                socket.BeginSend(buffer[0], 0, 1460, 0, new AsyncCallback(SendCallback), socket); // calls BeginSend if the buffer was empty before 
        }
    }
}

static void SendCallback(IAsyncResult ar)
{
    Console.WriteLine("in " + Thread.CurrentThread.ManagedThreadId);
    semaphore.WaitOne();
    Socket socket = (Socket)ar.AsyncState;
    lock(buffer)
    {
        buffer.RemoveAt(0); // removes data that was sent
        if (buffer.Count > 0) // if there is more data to send calls BeginSend again
            socket.BeginSend(buffer[0], 0, 1460, 0, new AsyncCallback(SendCallback), socket);
    }
    semaphore.Release();
    Console.WriteLine("out " + Thread.CurrentThread.ManagedThreadId);
}

这是输出:
enter image description here
因为线程10被转移到一个新的回调,没有给出前一个回调退出和清除信号量的机会,线程永远被锁定。

我该如何解决这个问题?

1 个答案:

答案 0 :(得分:2)

切换到任务:

好的msdn文章Tasks and the APM Pattern

public Task<int> SendAsync(Socket socket, byte[] buffer, int offset, int size, SocketFlags flags)
{
   var result = socket.BeginSend(buffer, offset, size, flags, _ => { }, socket);
   return Task.Factory.FromAsync(result,(r) => socket.EndSend(r));
}
现在事情变得容易一些:

使用默认BlockingCollection<>作为并发队列。它是线程保存并删除列表中的显式锁

static BlockingCollection<byte[]> buffer = new BlockingCollection<byte[]>();

public async void Main()
{
   Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
   socket.Connect(new IPEndPoint(new IPAddress(new byte[] { 192, 168, 1, 8 }), 123));
   while (!buffer.IsCompleted)
   {
      var data = buffer.Take();
      await SendAsync(socket, data, 0, data.Length, 0);
   }
   Console.ReadLine();            
}

在不需要信号量的情况下保持非阻塞发送和订单。

相关问题