C# 多线程生产者/消费者
我有一个代理服务器,我正在用C#编写。我还有一个Java小程序,它通过这个C#代理从视频服务器接收MJPEG数据。我的问题是,当视频服务器当前没有更多的MJPEG数据可用时,代理卡在阻塞读取调用中,我无法终止它C# 多线程生产者/消费者,c#,producer-consumer,C#,Producer Consumer,我有一个代理服务器,我正在用C#编写。我还有一个Java小程序,它通过这个C#代理从视频服务器接收MJPEG数据。我的问题是,当视频服务器当前没有更多的MJPEG数据可用时,代理卡在阻塞读取调用中,我无法终止它 // write the forwarded output // blocking on remoteServerResponseStream.Read while (m_running && (read = remoteServerResponseStream.Read
// write the forwarded output
// blocking on remoteServerResponseStream.Read
while (m_running && (read = remoteServerResponseStream.Read(buffer, 0, buffer.Length)) > 0)
{
bytesRead += read;
output.Write(buffer, 0, read);
output.Flush();
}
这应该通过Java小程序关闭流来终止(上面代码中的变量output
)。但是,Java小程序无法关闭此连接,因为当代理卡在remoteServerResponseStream.Read
等待数据时,代理从未确认关闭请求
我在这个问题上纠缠了一个星期。我想我可能已经想到了一个解决办法,但我不确定它是否有效。我很想听到任何关于这方面的反馈
我的想法是在另一个线程上删除
removeServerResponseStream.Read
,并使用共享队列传输数据。线程将读取数据并将其放在队列中。然后,我的主线程会将队列中的任何可用数据转发到输出
。这样,我可以不断地检查output.CanWrite
是否变为false,在这种情况下,我可以中止读取线程(这是我知道的唯一中断阻塞流读取的方法)。这是一个可行的解决方案吗?如果是这样,我应该不断轮询队列中的可用数据,还是创建一个事件?我很想听听大家对这个问题的看法!提前感谢。在这种情况下,我只是从另一个线程结束流阅读器。当你关闭读卡器时,它将中断循环。通过额外的同步队列只是不必要地做更多的样板。最终,你必须在某个地方停止阻塞读卡器。将值放入队列只会阻止您的消费者,而不会阻止您的阅读器
下面是一个套接字阻止读取的示例。我将阻止读取1字节,我将故意不发送。几秒钟后,我将处理该套接字,该套接字将退出其阻塞读取,应用程序将优雅地退出。我将记录每个线程正在做什么以及何时发生(每个日志行前面的数字是线程id,我将缩进单独的线程)
在使用阻塞读取器的类中,您应该实现一次性模式,并在那里关闭/处置读取器
static void Main(string[] args)
{
SocketTest();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
public static void SocketTest()
{
int port = 22345;
var tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
// Listening thread
new Thread(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Waiting for connection to port");
var socket = tcpListener.AcceptSocket();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Connection accepted");
var stream = new NetworkStream(socket);
var reader = new BinaryReader(stream);
try
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Starting blocking read");
var bytes = reader.ReadBytes(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Done blocking read, read {0} bytes", bytes.Length);
}
catch (Exception ex)
{
Console.WriteLine("Error reading " + ex);
}
}).Start();
// connecting thread
new Thread(() =>
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local port");
socket.Connect("127.0.0.1", port);
Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local succeeded");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Disposing of socket");
socket.Dispose();
}).Start();
Thread.Sleep(TimeSpan.FromSeconds(5));
}
运行此操作时,您会得到:
3 - Waiting for connection to port
4 - Connecting to local port
4 - Connecting to local succeeded
3 - Connection accepted
3 - Starting blocking read
4 - Disposing of socket
3 - Done blocking read, read 0 bytes
Press any key to exit
不过,你自己在这里回答了这个问题:
我的想法是使用removeServerResponseStream。阅读另一个线程
这正是你应该做的。当您知道应用程序已关闭时,请从活动线程关闭套接字。这个空闲线程是阻塞线程,您可以优雅地结束它
这里的模式是,您通常为特定套接字启动一个线程,并维护一个活动线程,该线程是一种请求的“控制器”线程 您应该使用.NET 4.0任务并行库引入的并发集合和其他容器类。我们需要额外的代码。我的问题是我不知道何时处理套接字。当视频剪辑结束时,视频服务器停止发送数据,但保持连接活动,因为视频服务器可以接收命令以查找剪辑中的不同位置。例如,如果它接收到一个命令,从一开始就搜索剪辑,它只会再次开始通过相同的连接发送数据。因此,不通过连接发送数据并不一定意味着我们应该关闭它。当Java小程序关闭
输出时,我需要关闭连接。听起来您确实知道何时处理套接字:当小程序关闭时。如果无法确定小程序何时断开连接,则可以在套接字上指定一个超时。如果您在已知时间内没有获得任何数据,套接字将自动关闭,但如果该值不正确,您将不得不重新连接。你能让你的小程序有一个ping吗?如果在一定时间内没有收到ping,控制器线程可以关闭相应的打开的套接字。那么我就有点困在使用共享队列中了,对吗?要将数据从读取线程传递到“使用者”或“输出”线程?另外,如果我关闭了与Java小程序的连接,我的“输出”线程中是否会出现异常,从那里我可以正确地处理读取套接字?顺便说一句,谢谢你的帮助。不,看看我的例子。没有引发异常,这是标准的套接字关闭过程。您不需要共享队列,仅仅因为数据在另一个线程中并不意味着您必须对其进行排队。您的对象在线程中共享。如果您所做的只是读取数据,然后将其写出来,只要该编写器未在其他线程中使用,您就可以了。感谢您的帮助devshorts。