C#实现生产者消费者队列

王朝学院·作者佚名  2016-08-28
窄屏简体版  字體: 小  |  中  |  大  |  超大  

开发过程中经常会碰到这样的场景:需要从一个地方获取一些数据,然后处理数据并将其保存在数据库中。

PRivate void FetchData() {}

private void SaveData() {}

static void Main(string[] args)

{

for (int i = 0; i < 10; i++)

{

FetchData(); // 获取数据

SaveData(); // 处理并保存

}

}

例如上述代码例子这样顺序执行,执行会很慢,原因是获取数据和处理并保存的过程都可能导致阻塞,然而FetchData()每次取数据并不需要等待上一条数据保存完成再进行。

这样的场景非常适合用生产者消费者队列:生产者就是FetchData(),用来生产数据;消费者SaveData(),用来消费数据。

举个实际例子,我们需要通过一个Web Api获取一些城市的天气情况,并将其保存到数据库中。

实现方式:

需要一个任务队列,生产者可以向队列中插入任务,消费者可以从任务队列中取出任务来执行。为保证线程安全,使用一个锁来保护这个队列的访问。制定一个退出策略,在所有任务完成时释放资源。下边是实现的完整代码:

classProgram

{//任务队列staticQueue<string> _tasks =newQueue<string>();//为保证线程安全,使用一个锁来保护_task的访问readonlystaticobject_locker =newobject();//通过 _wh 给工作线程发信号staticEventWaitHandle _wh =newAutoResetEvent(false);staticThread _worker;staticvoidMain(string[] args)

{//需要获取天气情况的城市对应代码varcityIds =newList<int> {101280601,101010100,101020100,101110101,101040100};//任务开始,启动工作线程_worker =newThread(Work);

_worker.Start();//生产者将数据插入队里中,并给工作线程发信号foreach(varcityIdincityIds)

EnqueueTask(FetchData(cityId));//任务结束Dispose();

}///<summary>执行工作</summary>staticvoidWork()

{while(true)

{stringwork =null;lock(_locker)

{if(_tasks.Count >0)

{

work= _tasks.Dequeue();//有任务时,出列任务if(work ==null)//退出机制:当遇见一个null任务时,代表任务结束return;

}

}if(work !=null)

SaveData(work);//任务不为null时,处理并保存数据else_wh.WaitOne();//没有任务了,等待信号}

}///<summary>插入任务</summary>staticvoidEnqueueTask(stringtask)

{lock(_locker)

_tasks.Enqueue(task);//向队列中插入任务_wh.Set();//给工作线程发信号}///<summary>结束释放</summary>staticvoidDispose()

{

EnqueueTask(null);//插入一个Null任务,通知工作线程退出_worker.Join();//等待工作线程完成_wh.Close();//释放资源}///<summary>获取数据</summary>staticstringFetchData(intcityId)

{varwc =newWebClient { Encoding =Encoding.UTF8 };varurl =string.Format("http://www.weather.com.cn/adat/sk/{0}.html", cityId);returnwc.DownloadString(url);

}///<summary>处理保存</summary>staticvoidSaveData(stringdata)

{varweatherInfo = (JsonConvert.DeserializeObject(data,typeof(Dictionary<string, Weatherinfo>))asDictionary<string, Weatherinfo>)["weatherinfo"];

Console.WriteLine("[{0}]:{1} 气温({2}) 风向({3}) 风力({4})", weatherInfo.Time, weatherInfo.City, weatherInfo.Temp, weatherInfo.Wd, weatherInfo.Ws);

Thread.Sleep(200);//模拟数据保存}

}publicclassWeatherinfo

{publicstringCity {get;set; }publicstringTemp {get;set; }publicstringTime {get;set; }publicstringWd {get;set; }publicstringWs {get;set; }

}

}

解释:

Main方法中,我们首先启动了一个工作线程,由于此时队列中没有任务,因此工作线程在等待信号。通过EnqueueTask向队列中插入任务,并通过等待句柄_wh发信号给工作线程,工作线程收到信号后就开始执行处理保存。当生产者获取完所有数据时,插入null任务,并等待工作线程完成。工作线程最后执行到null任务时退出。

参考:Threading in C# --> 中文翻译

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航