套接字跨线程锁定

时间:2011-05-30 19:29:59

标签: c# .net multithreading sockets parallel-processing

在我的ReceiveCallBack中,一个异步套接字是一个好主意Lock()那里的套接字?我问,因为另一个线程可能同时在套接字上发送数据。

private void ReceiveCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;

lock(client)
{
    int bytesRead = client.EndReceive(ar);
    // do some work

    // Kick off socket to receive async again.
    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            new AsyncCallback(ReceiveCallback), state);
}
}    

// This is commonly called by another thread
public void SendMessage(string cmdName, Object data)
{
    lock (client)
    {
         client.Send(arrayofdata, 0, arraylength, 0);
    }
}

2 个答案:

答案 0 :(得分:4)

如果您想使其具有线程安全性并且能够同时发送和接收,则需要创建两个锁定同步对象:

private readonly object sendSyncRoot = new object();
private readonly object receiveSyncRoot = new object();

private void ReceiveCallback(IAsyncResult ar)
{
    StateObject state = (StateObject)ar.AsyncState;
    Socket client = state.workSocket;

    lock (receiveSyncRoot)
    {
        int bytesRead = client.EndReceive(ar);
        // do some work

        // Kick off socket to receive async again.
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
    }
}

// This is commonly called by another thread
public void SendMessage(string cmdName, Object data)
{
    lock (sendSyncRoot)
        client.Send(arrayofdata, 0, arraylength, 0);
}

通常最好使用专用的syncRoot对象,而不是锁定其他类或成员。这样可以避免细微的死锁。

答案 1 :(得分:3)

没有。不要那样做。处理套接字的最佳方法是封装。不要将它暴露给除了声明它的类之外的任何东西。通过这样做,可以很容易地确保一次只有一个接收待处理。不需要使用锁。

至于发送。做这样的事情:

public class MyClient
{
    private readonly Queue<byte[]> _sendBuffers = new Queue<byte[]>();
    private bool _sending;
    private Socket _socket;

    public void Send(string cmdName, object data)
    {
        lock (_sendBuffers)
        {
            _sendBuffers.Enqueue(serializedCommand);
            if (_sending) 
                return;

            _sending = true;
            ThreadPool.QueueUserWorkItem(SendFirstBuffer);
        }
    }

    private void SendFirstBuffer(object state)
    {
        while (true)
        {
            byte[] buffer;
            lock (_sendBuffers)
            {
                if (_sendBuffers.Count == 0)
                {
                    _sending = false;
                    return;
                }

                buffer = _sendBuffers.Dequeue();
            }

            _socket.Send(buffer);
        }
    }
}

此方法不会阻止任何呼叫者,并且依次处理所有发送请求。