我正在编写一个程序,它监听传入的TcpClient并在数据到达时处理数据。 Listen()
方法在组件内的单独线程上运行,因此它需要是线程安全的。如果我在break
语句中的do
while
循环中lock()
,是否会释放锁定?如果没有,我该如何做到这一点?
谢谢!
(欢迎关于异步TCP套接字主题的任何其他建议。)
private void Listen()
{
do
{
lock (_clientLock)
{
if (!_client.Connected) break;
lock (_stateLock)
{
if (!_listening) break;
if (_client.GetStream().DataAvailable) HandleData();
}
}
Thread.Sleep(0);
} while (true);
}
答案 0 :(得分:21)
是。 lock语句转换为try / finally子句。例如,在C#4中,锁定语句如下:
lock(obj)
{
// body
}
粗略地将(taken from Eric Lippert's blog here)翻译为:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
{
// body
}
}
finally
{
if (lockWasTaken)
Monitor.Exit(temp);
}
当执行离开lock {}
的范围时,底层锁将自动释放。无论你如何退出范围(break / return / etc),都会发生这种情况,因为对Monitor.Exit的调用是在try / finally的finally块内部包装的。
答案 1 :(得分:3)
是的,锁将被释放。您可以使用ILDASM或Reflector查看实际生成的代码。 lock语句是以下代码的简写(粗略地)。
Monitor.Enter(_client);
try
{
// do your stuff
}
finally {
Monitor.Exit(_client);
}
请注意,始终执行finally块。
答案 2 :(得分:1)
退出lock{}
后,它会解锁您锁定的内容(就像在这方面的使用声明一样)。你退出的地方(开头,结尾或中间)并不重要,而是你完全放弃了锁的范围。想想如果你在中间引发异常会发生什么。
答案 3 :(得分:1)
因为你要求其他建议......我注意到你正在筑巢锁。这本身并不一定是坏事。但是,这是我注意的一面红旗。如果您在代码的另一部分中以不同的顺序获取这两个锁,则可能会出现死锁。我并不是说您的代码有任何问题。这只是值得注意的事情,因为它很容易出错。
答案 4 :(得分:0)
回答你问题的另一半:
欢迎任何有关异步TCP套接字主题的其他建议
简单地说,我不会以原始帖子所展示的方式管理这个。而是从System.Net.Sockets.TcpClient和System.Net.Sockets.TcpListener类中寻求帮助。使用像BeginAcceptSocket(...)和BeginRead(...)这样的异步调用,并允许ThreadPool完成它的工作。用这种方式拼凑起来真的很容易。
你应该能够实现你想要的所有服务器行为,而无需编写可怕的单词“new Thread”:)
以下是该想法的基本示例,减去了正常关闭,异常处理等的想法:
public static void Main()
{
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, 8080));
listener.Start();
listener.BeginAcceptTcpClient(OnConnect, listener);
Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
static void OnConnect(IAsyncResult ar)
{
TcpListener listener = (TcpListener)ar.AsyncState;
new TcpReader(listener.EndAcceptTcpClient(ar));
listener.BeginAcceptTcpClient(OnConnect, listener);
}
class TcpReader
{
string respose = "HTTP 1.1 200\r\nContent-Length:12\r\n\r\nHello World!";
TcpClient client;
NetworkStream socket;
byte[] buffer;
public TcpReader(TcpClient client)
{
this.client = client;
socket = client.GetStream();
buffer = new byte[1024];
socket.BeginRead(buffer, 0, 1024, OnRead, socket);
}
void OnRead(IAsyncResult ar)
{
int nBytes = socket.EndRead(ar);
if (nBytes > 0)
{
//you have data... do something with it, http example
socket.BeginWrite(
Encoding.ASCII.GetBytes(respose), 0, respose.Length, null, null);
socket.BeginRead(buffer, 0, 1024, OnRead, socket);
}
else
socket.Close();
}
}
有关如何执行此操作的更复杂的示例,请参阅我之前写的SslTunnel Library。