C#中可以通过管道流实现多线程之间数据交换。C#的管道氛围服务器端和客户端,二者均可配置为输入/输出/双向。服务器管道流监听客户管道流的连接请求。通常是一个线程间里服务器管道流,另一个线程建立客户管道流。二者建立连接之后,一条位于两个线程之间的管道流就形成了。
服务器管道流的使用线程代码示例片段如下:
class ServerThread
{
public ServerThread(string pipeName)
{
pipedStream = new NamedPipeServerStream(pipeName);
}
public void run()
{
Console.WriteLine(">> SERVER_THREAD: waiting for client connection ...");
pipedStream.WaitForConnection();
Console.WriteLine(" done.");
byte[] buffer = new byte[BUFFER_LENGTH];
int readCount;
do {
readCount = pipedStream.Read(buffer, 0, BUFFER_LENGTH);
if (readCount > 0)
{
string recvMsg = new String(System.Text.Encoding.Default.GetChars(buffer, 0, readCount));
Console.WriteLine(">> SERVER_RECV: " + recvMsg);
byte[] sendData = System.Text.Encoding.Default.GetBytes("SERVER_RETURN: " + recvMsg);
pipedStream.Write(sendData, 0, sendData.Length);
}
} while(readCount > 0);
}
public void stop()
{
pipedStream.Close();
pipedStream.Dispose();
pipedStream = null;
}
private NamedPipeServerStream pipedStream;
private const int BUFFER_LENGTH = 1024;
}
客户端管道流使用代码示例片段如下:
class ClientThread
{
public ClientThread(string pipeName)
{
pipedStream = new NamedPipeClientStream(pipeName);
}
public void run()
{
Console.Write(">> CLIENT_THREAD: connecting to server pipe stream ...");
pipedStream.Connect();
Console.WriteLine(" done.");
byte[] buffer = new byte[BUFFER_LENGTH];
int loopCount = 1;
int readCount;
do
{
string sendMsg = "CLIENT_SEND: the loop count is: " + loopCount;
byte[] sendData = System.Text.Encoding.Default.GetBytes(sendMsg);
pipedStream.Write(sendData, 0, sendData.Length);
loopCount++;
readCount = pipedStream.Read(buffer, 0, BUFFER_LENGTH);
if (readCount > 0)
{
string recvMsg = new String(System.Text.Encoding.Default.GetChars(buffer, 0, readCount));
Console.WriteLine(">> CLIENT_RECV: " + recvMsg);
}
} while (loopCount <= 5);
stop();
}
public void stop()
{
pipedStream.Close();
pipedStream.Dispose();
pipedStream = null;
}
private NamedPipeClientStream pipedStream;
private const int BUFFER_LENGTH = 1024;
}
主程序代码如下:
class MultiThreadExchange
{
public void run()
{
Console.Write("creating thread ...");
ServerThread serverThreadInstance = new ServerThread(pipeName);
Thread serverThread = new Thread(new ThreadStart(serverThreadInstance.run));
serverThread.Start();
ClientThread clientThreadInstance = new ClientThread(pipeName);
Thread clientThread = new Thread(new ThreadStart(clientThreadInstance.run));
clientThread.Start();
Console.WriteLine(" done.");
}
private const string pipeName = "testPipe";
}
程序运行后,输出结果如下:
creating thread ...>> SERVER_THREAD: waiting for client connection ...
done.
>> CLIENT_THREAD: connecting to server pipe stream ... done.
done.
>> SERVER_RECV: CLIENT_SEND: the loop count is: 1
>> CLIENT_RECV: SERVER_RETURN: CLIENT_SEND: the loop count is: 1
>> SERVER_RECV: CLIENT_SEND: the loop count is: 2
>> CLIENT_RECV: SERVER_RETURN: CLIENT_SEND: the loop count is: 2
>> SERVER_RECV: CLIENT_SEND: the loop count is: 3
>> CLIENT_RECV: SERVER_RETURN: CLIENT_SEND: the loop count is: 3
>> SERVER_RECV: CLIENT_SEND: the loop count is: 4
>> CLIENT_RECV: SERVER_RETURN: CLIENT_SEND: the loop count is: 4
>> SERVER_RECV: CLIENT_SEND: the loop count is: 5
>> CLIENT_RECV: SERVER_RETURN: CLIENT_SEND: the loop count is: 5
需要注意的是:NamedPipeServerStream.WaitForConnection()和NamedPipeClientStream.Connect()操作都是阻塞操作,在完成服务器端和客户端的管道连接之前,执行这两个函数的线程会被一直阻塞。感觉这就像网络连接一样。不明白MS为什么要把C#的管道流设计成这样,用起来这么麻烦,可能是考虑与Windows的管道API对应吧。
个人还是比较喜欢Java的管道流的操作方式,分别创建输入管道流对象和输出管道流对象,然后把两个管道流connect一下就行了。