示例-05 - ManualThreadingDemo
手工多线程(客户端副本)demo
源码
using System;
using System.Threading;
using System.Threading.Tasks;
using NetMQ;
using NetMQ.Sockets;
namespace ManualThreadingDemo //手动线程
{
public class Program
{
public void Run()
{//当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本
//,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
// ThreadLocal是线程局部变量,而线程局部变量在多线程场景中应用广泛。
//NOTES
//1. Use ThreadLocal<DealerSocket> where each thread has
// its own client DealerSocket to talk to server
//2. Each thread can send using it own socket
//3. Each thread socket is added to poller
//1. 使用每个线程都有的 ThreadLocal<DealerSocket>
// 它自己的客户端 DealerSocket 与服务器对话
//2。每个线程都可以使用自己的套接字发送
//3. 每个线程套接字都添加到轮询器
ThreadLocal<DealerSocket> clientSocketPerThread =
new ThreadLocal<DealerSocket>(); //每个线程客户端套接字 经销商套接字
int delay = 3000;
Poller poller = new Poller();//套接字轮询器
using (NetMQContext ctx = NetMQContext.Create())
{
using (var server = ctx.CreateRouterSocket())//服务器端 路由器套接字
{
server.Bind("tcp://127.0.0.1:5556");
//start some threads, each with its own DealerSocket
//to talk to the server socket. Creates lots of sockets,
//but no nasty race conditions no shared state, each
//thread has its own socket, happy days
//启动一些线程,每个线程都有自己的 DealerSocket 与服务器套接字对话。创建很多套接字,
//但没有讨厌的竞争条件 没有共享状态, 每个线程都有自己的套接字,快乐的日子
for (int i = 0; i < 3; i++)
{
Task.Factory.StartNew((state) =>
{
DealerSocket client = null;//客户端套接字
if (!clientSocketPerThread.IsValueCreated)//未在当前线程创建客户端套接字
{
client = ctx.CreateDealerSocket();//创建一个客户端套接字
client.Connect("tcp://127.0.0.1:5556");
client.ReceiveReady += Client_ReceiveReady;//绑定接收就绪事件处理方法
clientSocketPerThread.Value = client;//设置当前线程客户端套接字副本
poller.AddSocket(client);//添加到轮询器
}
else
{
client = clientSocketPerThread.Value;//已经创建郭当前线程的客户端套接字,直接获取它
}
while (true)//该线程客户端重复发送字符串 client i 给服务器
{
var messageToServer = new NetMQMessage(); //创建消息
messageToServer.AppendEmptyFrame();//空帧
messageToServer.Append(state.ToString());//字符串
client.SendMessage(messageToServer);//客户端发送给服务器时自带 地址。路由器套接字
Thread.Sleep(delay);
}
},string.Format("client {0}", i), TaskCreationOptions.LongRunning);
}
//start the poller 开启轮询任务
Task task = Task.Factory.StartNew(poller.Start);
//服务器循环
while (true)
{
var clientMessage = server.ReceiveMessage(); //接收每个线程的客户端发来的消息
Console.WriteLine("========================");
Console.WriteLine(" INCOMING CLIENT MESSAGE ");
Console.WriteLine("========================");
for (int i = 0; i < clientMessage.FrameCount; i++)
{
Console.WriteLine("Frame[{0}] = {1}", i,
clientMessage[i].ConvertToString());
}
if (clientMessage.FrameCount == 3)
{
var clientAddress = clientMessage[0];
var clientOriginalMessage = clientMessage[2].ConvertToString();
string response = string.Format("{0} back from server {1}",
clientOriginalMessage, DateTime.Now.ToLongTimeString());
var messageToClient = new NetMQMessage();
messageToClient.Append(clientAddress);//服务器发送给客户端时手动添加地址到消息。
messageToClient.AppendEmptyFrame();
messageToClient.Append(response);
server.SendMessage(messageToClient);
}
}
}
}
}
void Client_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
bool hasmore = false;
e.Socket.Receive(out hasmore);
if (hasmore)
{
string result = e.Socket.ReceiveString(out hasmore);
Console.WriteLine("REPLY " + result);
}
}
[STAThread]
public static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
}
}
示例-06 - Divide And Conquer
推拉模型demo
运行演示
源码
#Ventilator
using System;
using NetMQ;
namespace Ventilator//任务分发
{
public class Program
{
public static void Main(string[] args)
{
// Task Ventilator
// Binds PUSH socket to tcp://localhost:5557
// Sends batch of tasks to workers via that socket
//将 PUSH 套接字绑定到 tcp://localhost:5557 通过该套接字向工作人员发送一批任务
Console.WriteLine("====== VENTILATOR ======");
using (NetMQContext ctx = NetMQContext.Create())
{
//用于发送消息的套接字 Push套接字
using (var sender = ctx.CreatePushSocket())//Push1
{
sender.Bind("tcp://*:5557");
using (var sink = ctx.CreatePushSocket())//Push2
{
sink.Connect("tcp://localhost:5558");//连接 5558 PullSocket
Console.WriteLine("Press enter when worker are ready");
Console.ReadLine();
//the first message it "0" and signals start of batch
//see the Sink.csproj Program.cs file for where this is used
Console.WriteLine("Sending start of batch to Sink");
sink.Send("0");//Push2发送
Console.WriteLine("Sending tasks to workers");
//initialise random number generator
Random rand= new Random(0);
// 预期成本 Ms
int totalMs = 0;
//send 100 tasks (workload for tasks, is just some random sleep time that
//the workers can perform, in real life each work would do more than sleep
for (int taskNumber = 0; taskNumber < 100; taskNumber++)
{
//从 1 到 100 毫秒的随机工作负载
int workload = rand.Next(0, 100);
totalMs += workload;
Console.WriteLine("Workload : {0}", workload);
sender.Send(workload.ToString()); //Push1发送
}
Console.WriteLine("Total expected cost : {0} msec", totalMs);
Console.WriteLine("Press Enter to quit");
Console.ReadLine();
}
}
}
}
}
}
#Worker
using System;
using System.Threading;
using NetMQ;
namespace Worker //工作线程
{
public class Program
{
public static void Main(string[] args)
{
// Task Worker
// Connects PULL socket to tcp://localhost:5557
// collects workload for socket from Ventilator via that socket
// Connects PUSH socket to tcp://localhost:5558
// Sends results to Sink via that socket
Console.WriteLine("====== WORKER ======");
using (NetMQContext ctx = NetMQContext.Create())
{
//用于发送消息的套接字
using (var receiver = ctx.CreatePullSocket())
{
receiver.Connect("tcp://localhost:5557");
//用于发送消息的套接字
using (var sender = ctx.CreatePushSocket())
{
sender.Connect("tcp://localhost:5558");
//永远处理任务
while (true)
{
//workload from the vetilator is a simple delay
//to simulate some work being done, see
//Ventilator.csproj Proram.cs for the workload sent
//In real life some more meaningful work would be done
string workload = receiver.ReceiveString();
//simulate some work being done
Thread.Sleep(int.Parse(workload));
//send results to sink, sink just needs to know worker
//is done, message content is not important, just the precence of
//a message means worker is done.
//See Sink.csproj Proram.cs
Console.WriteLine("Sending to Sink");
sender.Send(string.Empty);
}
}
}
}
}
}
}
#Sink
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using NetMQ;
namespace Sink
{
public class Program//推拉模型
{
public static void Main(string[] args)
{
// Task Sink
// Bindd PULL socket to tcp://localhost:5558
// Collects results from workers via that socket
//将 PULL 套接字绑定到 tcp://localhost:5558 通过该套接字从workers那里收集结果
Console.WriteLine("====== SINK ======");
using (NetMQContext ctx = NetMQContext.Create())
{
//接收消息的套接字
using (var receiver = ctx.CreatePullSocket())
{
receiver.Bind("tcp://localhost:5558");
//等待批处理开始(参见 Ventilator.csproj Program.cs)
var startOfBatchTrigger = receiver.ReceiveString();//批处理触发器的开始 。Ventilator的sink 发来 0.
Console.WriteLine("Seen start of batch");
//计时
Stopwatch watch = new Stopwatch();
watch.Start();
for (int taskNumber = 0; taskNumber < 100; taskNumber++)
{
var workerDoneTrigger = receiver.ReceiveString();//worker 发来 空帧
if (taskNumber % 10 == 0)//十分之一
{
Console.Write(":");
}
else
{
Console.Write(".");//十分之九
}
}
watch.Stop();
//计算和报告批处理的持续时间
Console.WriteLine();
Console.WriteLine("Total elapsed time {0} msec", watch.ElapsedMilliseconds);
Console.ReadLine();
}
}
}
}
}
The End