Boost多线程-替换MFC线程
方便之处:直接使用结构体传入函数参数,供函数使用。
使用boost多线程,boost库给出了一个相对理想的多线程使用组合库。
参考链接:http://blog.csdn.net/iamnieo/article/details/2908621
一:使用参数的boost进程:
(1):建立参数类
class BoostThreadFunc {
public:
pcl::PointCloud<pcl::PointXYZRGB> ModelCloud;
MyRect BBX;
CAviTestDlg* dlg; //使用主框的变量
public:
BoostThreadFunc();//构造函数....
BoostThreadFunc( pcl::PointCloud<pcl::PointXYZRGB> &ModelCloud , MyRect &BBX , CAviTestDlg* dlg ) {
this->BBX = BBX;
this->ModelCloud =ModelCloud;
this->dlg = dlg; }//重载构造函数....
void run(
pcl::PointCloud<pcl::PointXYZRGB> &ModelCloud,
MyRect &BBX,
CAviTestDlg* &dlg );//主要运行函数....
//创建函数对象....
void operator()() { this->run( this->ModelCloud, this->RatioRange, this->BBX, this->dlg); }
};
void BoostThreadFunc ::run(
pcl::PointCloud<pcl::PointXYZRGB> &ModelCloud,
MyRect &BBX,
CAviTestDlg* &dlg )
{
.............................................................
}//主要运行函数....
(2):创建一个线程:创建线程
boost::thread myThread(threadFun);
需要注意的是:参数可以是函数对象或者函数指针。并且这个函数无参数,并返回void类型。
所以,取巧的方式是把参数放入 构造函数里面,使用函数对象调用运行函数。
使用方式:
BoostThreadFunc MyThreadFunc( *ModelCloud, BBX, this);//在主框里调用boost线程.
boost::thread MyThread( MyThreadFunc);
MyThread.join();
注意:使用的过程中脑残了一番:boost::thread MyThread(& MyThreadFunc); 造成thread.hpp的f()函数无法展开!什么原因???
是否?boost::thread MyThread( MyThreadFunc);载入的是无参数函数对象/函数指针,而boost::thread MyThread(& MyThreadFunc);中若使用&必须绑定全局静态函数.
二:参考链接:http://www.cnblogs.com/younes/archive/2010/06/06/1752745.html
使用线程组
如果你需要创建几个线程,考虑使用一个线程组对象thread_group来组织它们。一个thread_group对象可以使用多种方法管理线程。首先,可以使用一个指向动态创建的线程对象的指针作为参数来调用add_thread方法,将这个线程加入线程组。也可以直接使用线程组类的create_thread方法,可不先创建线程而直接把线程加入到线程组中。
当线程组对象的析构函数被调用时,它将删除(delete)所有这些通过add_thread方法加入的线程指针。所以,只能将堆上的线程对象指针通过add_thread方法加入线程组。remove_thread方法从线程组删除某个线程的指针,但是我们仍需负责把线程本身内存释放掉。
线程组对象的成员方法join_all方法等待线程组中所有线程结束,才返回。
boost::thread_group grp;
boost::thread *p =newboost::thread(threadFun);
grp.add_thread(p);
//do something...
grp.remove_thread(p); grp.create_thread(threadFun);
grp.create_thread(threadFun); //Now there are two threads in grp grp.join_all(); //Wait for all threads to finish
三、使资源是线程安全的
保证同一时刻多个线程不会同时修改同一个共享资源,那么这个程序是线程安全的,或者是串行化访问资源的。可以使用mutex类来控制线程的并发问题。
| 0 |
#include <iostream>#include <boost/thread/thread.hpp>#include <string>// A simple queue class; don't do this, use std::queuetemplate<typenameT>classQueue {public: Queue( ) {} ~Queue( ) {} voidenqueue(constT& x) { boost::mutex::scoped_lock lock(mutex_); list_.push_back(x);// A scoped_lock is automatically destroyed (and thus unlocked)// } T dequeue( ) { boost::mutex::scoped_lock lock(mutex_); if(list_.empty( )) throw"empty!"; // This leaves the current T tmp = list_.front( );// lock is released list_.pop_front( ); return(tmp); }// Again: when scope ends, mutex_ is unlockedprivate: std::list<T> list_; boost::mutex mutex_;};Queue<std::string> queueOfStrings;void sendSomething( ) { std::string s; for(inti = 0; i < 10; ++i) { queueOfStrings.enqueue("Cyrus"); }}void recvSomething( ) { std::string s; for(inti = 0; i < 10; ++i) { try{s = queueOfStrings.dequeue( );} catch(...) {} }}intmain( ) { boost::threadthr1(sendSomething); boost::threadthr2(recvSomething); thr1.join( ); thr2.join( );} |
mutex对象本身并不知道它代表什么,它仅仅是被多个消费者线程使用的资源访问的锁定解锁标志。在某个时刻,只有一个线程可以锁定这个mutex对象,这就阻止了同一时刻有多个线程并发访问共享资源。一个mutex就是一个简单的信号机制。
给mutex加解锁有多种策略,最简单的是使用scoped_lock类,它使用一个mutex参数来构造,并一直锁定这个mutex直到对象被销毁。如果这个正在被构造的mutex已经被别的线程锁定的话,当前线程就会进入wait状态,直到这个锁被解开。
三、读写锁
mutex有一个美中不足,它不区分读和写。线程如果只是进行读操作,mutex强制线程串行化访问资源,效率低。而且这种操作不需要排他性访问。基于这个原因,Boost线程库提供了read_write_mutex。
| 0 |
#include <iostream>#include <boost/thread/thread.hpp>#include <boost/thread/read_write_mutex.hpp>#include <string>template<typenameT>classQueue {public: Queue( ) :
~Queue( ) {} voidenqueue(constT& x) { boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_); list_.push_back(x); } T dequeue( ) { // Again, use a write lock boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_); if(list_.empty( )) throw"empty!"; T tmp = list_.front( ); list_.pop_front( ); return(tmp); } T getFront( ) { // This is a read-only operation, so you only need a read lock boost::read_write_mutex::scoped_read_lock readLock(rwMutex_); if(list_.empty( )) throw"empty!"; return(list_.front( )); }private: std::list<T> list_; boost::read_write_mutex rwMutex_;};Queue<std::string> queueOfStrings;void sendSomething( ) { std::string s; for(inti = 0; i < 10; ++i) { queueOfStrings.enqueue("Cyrus"); }}void checkTheFront( ) { std::string s; for(inti = 0; i < 10; ++i) { try{s = queueOfStrings.getFront( );} catch(...) {} }}int main( ) { boost::threadthr1(sendSomething); boost::thread_group grp; grp.create_thread(checkTheFront); grp.create_thread(checkTheFront); grp.create_thread(checkTheFront); grp.create_thread(checkTheFront); thr1.join( ); grp.join_all( );} |
注意Queue的构造函数中队读写锁rwMutex的初始化。同一时刻,可能有多个读写线程要锁定一个read_write_mutex,而这些锁的调度策略依赖于构造这个mutex时选定的调度策略。Boost库中提供了四种调度策略:
1)reader_priority:等待读锁的线程优先于等待写锁的线程
2)writer_priority:等待写锁的线程优先于等待读锁的线程
3)alternating_single_read:在读锁和写锁之间交替
4)alternating_many_reads:在读锁和写锁之间交替,这个策略将在两个写锁之间使得所有的在这个queue上挂起的读锁都被允许。
选择使用哪种策略要慎重,因为使用前两种的话可能会导致某些锁始终不能成功,出现饿死的现象。
死锁、饿死和竞态条件
1)死锁,是涉及至少2个线程和2个资源的情况。线程A和B,资源X和Y。A锁定了X,而B锁定了Y。此时A和B有彼此想要对方的资源,死锁就出现了。
死锁的预防有两种方法。一种是,通过小心的按照一定的顺序对不同的mutex来加锁。另一种是,使用Boost提供的try_mutex互斥量和scoped_try_lock。或者使用时间锁。scoped_try_lock对try_mutex加锁时,可能成功,也可能失败,但不会阻塞。时间锁则有一个超时时间。
0 booldequeue(T& x){boost::try_mutex::scope_try_lock lock(tryMutex_);if(!lock.locked())returnfalse;else{if(list_.empty())throw"empty!";x = list_.front();list_.pop_front();returntrue;}}private:boost::try_mutex tryMutex_;
2)饿死,如果你正在使用write_priority策略,并且你有很多创建写锁的线程,那么读锁的线程就可能饿死。
3)竞态条件,
0 if(q.getFront() =="Cyrus"){str = q.dequeue();//....}
这个代码在单线程环境中工作很好,因为q在第一行和第二行代码之间不会被修改。多线程环境中则会出现问题。此为竞态条件。解决的方法是为Queue添加一个成员函数dequeueIfEquals,在函数执行过程中始终锁定互斥量。
四、从一个线程中给另一个线程发送通知
当需要线程等待某个事物时,可以创建一个condition对象,然后通过这个对象来通知那些等待的线程。
| 0 |
#include <iostream>#include <boost/thread/thread.hpp>#include <boost/thread/condition.hpp>#include <boost/thread/mutex.hpp>#include <list>#include <string>classRequest {/*...*/};// A simple job queue class; don't do this, use std::queuetemplate<typenameT>classJobQueue {public: JobQueue( ) {} ~JobQueue( ) {} void submitJob(constT& x) { boost::mutex::scoped_lock lock(mutex_); list_.push_back(x); workToBeDone_.notify_one( ); } T getJob( ) { boost::mutex::scoped_lock lock(mutex_); workToBeDone_.wait(lock);// Wait until this condition is // satisfied, then lock the mutex T tmp = list_.front( ); list_.pop_front( ); return(tmp); }private: std::list<T> list_; boost::mutex mutex_; boost::condition workToBeDone_;};JobQueue<Request> myJobQueue;void boss( ) { for(;;) { // Get the request from somewhere Request req; myJobQueue.submitJob(req); }}void worker( ) { for(;;) { Request r(myJobQueue.getJob( )); // Do something with the job... }}int main( ) { boost::threadthr1(boss); boost::threadthr2(worker); boost::threadthr3(worker); thr1.join( ); thr2.join( ); thr3.join( );} |
boost::mutex::scoped_lock lock(mutex_);
workToBeDone_.wait(lock);
这两行代码,第一行锁定这个mutex对象。第二行代码解开这个mutex上的锁,然后进行等待或者休眠,直到它的条件得到了满足。这个mutex互斥对象的解锁让其他的线程能够使用这个mutex对象,它们中的某个需要设置这个等待条件,之后通知另外的线程。
notify_all函数,通知那些所有正在等待某个条件变为真的线程,那些线程随后进入运行状态。wait方法做两件事情:它一直等待直到有人在它正等待的condition上调用notify_one或notify_all,然后它就试图锁定相关的mutex。当调用的是notify_all时,尽管多个等待的线程都尽量去获得下一个锁,但谁将获得依赖于这个mutex的类型和使用的优先策略。
一个condition对象能让消费者线程休眠,因此在还没有碰到一个condition时处理器可以去处理别的事情。例如一个web服务器使用一个工作线程池来处理进来的请求。当没有需求进来时,让这些子线程处于等待状态比让它们循环的查询或者睡眠然后偶尔唤醒来检查这个队列,要好很多。
五、只初始化一次共享资源
| 0 |
#include <iostream>#include <boost/thread/thread.hpp>#include <boost/thread/once.hpp>// Some sort of connection class that should only be initialized oncestructConn { static void init( ) { ++i_; } staticboost::once_flag init_; staticint i_; // ...};int Conn::i_ = 0;boost::once_flag Conn::init_ = BOOST_ONCE_INIT;void worker( ) { boost::call_once(Conn::init, Conn::init_); // Do the real work...}Conn c; // You probably don't want to use a global, so see the // next Recipeintmain( ) { boost::thread_group grp; for(inti = 0; i < 100; ++i) grp.create_thread(worker); grp.join_all( ); std::cout << c.i_ <<'\n';// c.i_ = 1} |
一个共享资源不得不在某个地方被初始化,并且你希望第一次使用这个资源的线程来完成初始化工作。一个once_flag类型和call_once函数能够保证多个线程不会重复的初始化同一个对象。首先,必须使用BOOST_ONCE_INIT宏来初始化这个once_flag对象。boost::once_flag Conn::init_ = BOOST_ONCE_INIT; 之后调用call_once函数,boost::call_once(Conn::init, Conn::init_); 第一个形参是希望被执行一次的初始化函数的地址。
六、给线程函数传递一个参数
| 0 |
#include <iostream>#include <string>#include <functional>#include <boost/thread/thread.hpp>// A typedef to make the declarations below easier to readtypedefvoid(*WorkerFunPtr)(conststd::string&);template<typenameFunT, // The type of the function typenameParamT >// The type of its parameterstructAdapter { Adapter( FunT f, ParamT& p) :// Construct this adapter and set the f_(f), p_(&p) {} // members to the function and its arg void operator( )( ) {// This just calls the function with its arg f_(*p_); }private: FunT f_; ParamT* p_; // Use the parameter's address to avoid extra copying};void worker(const std::string& s) { std::cout << s <<'\n';}intmain( ) { std::string s1 ="This is the first thread!"; std::string s2 ="This is the second thread!"; boost::threadthr1(Adapter<WorkerFunPtr, std::string>(worker, s1)); boost::threadthr2(Adapter<WorkerFunPtr, std::string>(worker, s2)); thr1.join( ); thr2.join( );} |
使用这个函数适配器类模板,你就可以给线程函数传递参数了。如果你需要传递多个参数,仅需要在这个适配器中增加另一个类型和成员变量。
Boost多线程-替换MFC线程的更多相关文章
- MFC 线程创建方式
MFC 分UI线程和工作线程,一般现在的应用程序都是一个主UI线程和N个工作线程来完成工作.主UI线程获取到工作线程发送的信息来刷新界面. 不过因为工作需要,MFC有要维护的项目,因此就学习一下MFC ...
- VC中利用多线程技术实现线程之间的通信
当前流行的Windows操作系统能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力.用进程和线程的观点来研究软 ...
- Boost多线程编程
Boost多线程编程 背景 • 今天互联网应用服务程序普遍使用多线程来提高与多客户链接时的效率:为了达到最大的吞吐量,事务服务器在单独的线程上运行服务程序: GUI应用程序将那些费时, ...
- 【C/C++学院】0904-boost智能指针/boost多线程锁定/哈希库/正則表達式
boost_array_bind_fun_ref Array.cpp #include<boost/array.hpp> #include <iostream> #includ ...
- Python中的多线程编程,线程安全与锁(二)
在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...
- Boost多线程
一.概述 线程是在同一程序同一时间内允许执行不同函数的离散处理队列,这使得在一个长时间进行某种特殊运算的函数在执行时不阻碍其他的函数时变得十分重要.线程实际上允许同时执行两种函数,而这两者不必 ...
- boost多线程使用简例
原文链接:http://www.cppblog.com/toMyself/archive/2010/09/22/127347.html C++ Boost Thread 编程指南 转自cnblog: ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- .NET面试题解析(07)-多线程编程与线程同步
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实是很多的,比如多线程编程.线程上下文.异步编程.线程同步构造.GUI的跨线程访问等等, ...
随机推荐
- 03-Linux命令基础-第03天(makefile、静态库、动态库、gdb调试工具)
01- 复习 tar tvf xxx 查看压缩包内容 区分前后台: 是否能和用户交互 Vmware选桥接模式 会给系统虚拟一个和外部相同网段的ip 02- vim扩展操作 因为不是做嵌入式开发的 所以 ...
- GDI 画笔(9)
使用现有画笔 Windows 提供三种备用画笔(Stock Pen):BLACK_PEN(黑色画笔).WHITE_PEN(白色画笔).NULL_PEN(不绘制任何图形的画笔). 调用 GetStock ...
- Java代码模拟http请求的两种方式
z这里用百度地图的逆地理编码接口为例, 第一种方式:(通过jdk中的java.net包) 引入工具类 import java.net.URL; import java.net.URLConnectio ...
- GetBulkRequest PDU的应用
http://blog.csdn.net/wenph2008/article/details/16821617
- asp.net--WebService知识点
开头是这样的 [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = Wsi ...
- 洛谷 P2728 纺车的轮子 Spinning Wheels
P2728 纺车的轮子 Spinning Wheels 题目背景 一架纺车有五个纺轮(也就是五个同心圆),这五个不透明的轮子边缘上都有一些缺口.这些缺口必须被迅速而准确地排列好.每个轮子都有一个起始标 ...
- C# ArcgisEngine开发中,对一个图层进行过滤,只显示符合条件的要素
转自原文 C# ArcgisEngine开发中,对一个图层进行过滤,只显示符合条件的要素 有时候,我们要对图层上的地物进行有选择性的显示,以此来满足实际的功能要求. 按下面介绍的方法可轻松实现图层属性 ...
- CentOS 7 安装Nginx做反向代理
题记 须要使用nginx的反向代理功能,測试环境为centos+NGINX 1.8.0. 跳过一些繁琐的问题,直接记录核心 步骤 (1)centos 安装在VM中.因此须要注意网络连接问题 (2)安装 ...
- GammaRay 是一个允许你查看 Qt 应用程序甚至在某种程度上修改它的独特应用,可谓是 Debugger 的良好补充
GammaRay is a tool to poke around in a Qt-application and also to manipulate the application to some ...
- html5 初探
html5是越来越火了.小小菜鸟也来学习学习. 相比于之前的几个版本,HTML5提供了更加丰富的多媒体标签使得音乐,视频的播放不用再借助于flah了.不过暂时各浏览器间样式还是有差别. 除此之外,表单 ...