linux下getrlimit()与setrlimit()函数说明及使用

1.getrlimit()/setrlimit()函数

获取或设置资源使用限制,linux下每种资源都有相关的软硬限制,软限制是内核强加给相应资源的限制值,硬限制是软限制的最大值。非授权调用的进程只能将其软限制指定为0~硬限制范围中的某个值,同时能不可逆转地降低其硬限制。授权进程可以任意改变其软硬限制。RLIM_INFINITY:表示不对资源限制。

用法:

头文件

#include <sys/resource.h>

函数

  1. int getrlimit(int resource, struct rlimit *rlim);
  2.  
  3. int setrlimit(int resource, const struct rlimit *rlim);

struct rlimit结构体(描述软硬限制),原型如下:

  1. struct rlimit {
  2.   rlim_t rlim_cur;
  3.   rlim_t rlim_max;
  4. };

参数说明:

  1. resource:可能的选择有
  2.  
  3. RLIMIT_AS //进程的最大虚内存空间,字节为单位。
  4. RLIMIT_CORE //内核转存文件的最大长度。
  5. RLIMIT_CPU //最大允许的CPU使用时间,秒为单位。当进程达到软限制,内核将给其发送SIGXCPU信号,这一信号的默认行为是终止进程的执行。然而,可以捕捉信号,处理句柄可将控制返回给主程序。如果进程继续耗费CPU时间,核心会以每秒一次的频率给其发送SIGXCPU信号,直到达到硬限制,那时将给进程发送 SIGKILL信号终止其执行。
  6. RLIMIT_DATA //进程数据段的最大值。
  7. RLIMIT_FSIZE //进程可建立的文件的最大长度。如果进程试图超出这一限制时,核心会给其发送SIGXFSZ信号,默认情况下将终止进程的执行。
  8. RLIMIT_LOCKS //进程可建立的锁和租赁的最大值。
  9. RLIMIT_MEMLOCK //进程可锁定在内存中的最大数据量,字节为单位。
  10. RLIMIT_MSGQUEUE //进程可为POSIX消息队列分配的最大字节数。
  11. RLIMIT_NICE //进程可通过setpriority() 或 nice()调用设置的最大完美值。
  12. RLIMIT_NOFILE //指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误。
  13. RLIMIT_NPROC //用户可拥有的最大进程数。
  14. RLIMIT_RTPRIO //进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级。
  15. RLIMIT_SIGPENDING //用户可拥有的最大挂起信号数。
  16. RLIMIT_STACK //最大的进程堆栈,以字节为单位。

返回值说明:

  1. 成功执行时,返回0。失败返回-1,errno被设为以下的某个值
  2. EFAULT:rlim指针指向的空间不可访问
  3. EINVAL:参数无效
  4. EPERM:增加资源限制值时,权能不允许

2.修改task进程资源上限值(ulimit和setrlimit)

1)soft limit是指内核所能支持的资源上限。比如对于RLIMIT_NOFILE(一个进程能打开的最大文件数,内核默认是1024),soft limit最大也只能达到1024。对于RLIMIT_CORE(core文件的大小,内核不做限制),soft limit最大能是unlimited。
2)hard limit在资源中只是作为soft limit的上限。当你设置hard limit后,你以后设置的soft limit只能小于hard limit。要说明的是,hard limit只针对非特权进程,也就是进程的有效用户ID(effective user ID)不是0的进程。具有特权级别的进程(具有属性CAP_SYS_RESOURCE),soft limit则只有内核上限。

ubuntu下ulimit指令对比

ulimit -c -n -s  软限制   

ulimit -c -n -s -H 硬限制

备注:

 1. unlimited表示no limit, 即内核的最大值

 2. 当不指定limit的时候,该命令显示当前值。这里要注意的是,当你要修改limit的时候,如果不指定-S或者-H,默认是同时设置soft limit和hard limit。也就是之后设置时只能减不能增。所以,建议使用ulimit设置limit参数是加上-S。

getrlimit和setrlimit

注意:

       在使用setrlimit,需要检查是否成功来判断新值有没有超过hard limit。如下例Linux系统中在应用程序运行过程中经常会遇到程序突然崩溃,提示:Segmentation fault,这是因为应用程序收到了SIGSEGV信号。这个信号提示当进程发生了无效的存储访问,当接收到这个信号时,缺省动作是:终止w/core。终止w/core的含义是:在进程当前目录生成core文件,并将进程的内存映象复制到core文件中,core文件的默认名称就是“core”(这是 Unix类系统的一个由来已久的功能)。
事实上,并不是只有SIGSEGV信号产生coredump,还有下面一些信号也产生coredump:SIGABRT(异常终止)、SIGBUS(硬件故障)、SIGEMT(硬件故障)、SIGFPE(算术异常)、SIGILL(非法硬件指令)、SIGIOT(硬件故障),SIGQUIT,SIGSYS(无效系统调用),SIGTRAP(硬件故障)等。

例子

  1. #include <sys/resource.h>
  2. void init_core_dump()
  3. {
  4.     struct rlimit limit;
  5.  
  6.     memset(&limit, 0, sizeof(limit));
  7.     limit.rlim_cur = RLIM_INFINITY; //软限制,表示对资源没有限制
  8.     limit.rlim_max = RLIM_INFINITY; //硬限制,这个参数表示对资源没有限制,一定要大于等于rlim_cur值
  9.     setrlimit(RLIMIT_CORE, &limit);
  10. }
  11.  
  12. int main(void)
  13. {
  14. init_core_dump();
  15.  
  16. return 0;
  17. }

 

boost.asio系列——io_service

 

IO模型

io_service对象是asio框架中的调度器,所有异步io事件都是通过它来分发处理的(io对象的构造函数中都需要传入一个io_service对象)。

    asio::io_service io_service;
    asio::ip::tcp::socket socket(io_service);

在asio框架中,同步的io主要流程如下:

    

  1. 应用程序调用IO对象成员函数执行IO操作
  2. IO对象向io_service 提出请求.
  3. io_service 调用操作系统的功能执行连接操作.
  4. 操作系统向io_service 返回执行结果.
  5. io_service将错误的操作结果翻译为boost::system::error_code类型,再传递给IO对象.
  6. 如果操作失败,IO对象抛出boost::system::system_error类型的异常.

而异步IO的处理流程则有些不同:

    

  1. 应用程序调用IO对象成员函数执行IO操作
  2. IO对象请求io_service的服务
  3. io_service 通知操作系统其需要开始一个异步连接.
  4. 操作系统指示连接操作完成, io_service从队列中获取操作结果
  5. 应用程序必须调用io_service::run()以便于接收结果
  6. 调用io_service::run()后,io_service返回一个操作结果,并将其翻译为error_code,传递到事件回调函数中

io_service对象

io_servuce的作用: io_servie 实现了一个任务队列,这里的任务就是void(void)的函数。Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,run是执行队列中的任务,直到全部执行完毕,并且run可以被N个线程调用。Io_service是完全线程安全的队列。

io_service对象提供的接口有run、run_one、poll、poll_one、stop、reset、dispatch、post,最常用的是run、post、stop:

  1. post用于发布io事件,如timer,socket读写等,一般由asio框架相应对象调用,无需我们显式调用。
  2. run用于监听io事件响应,并执行响应回调,对于异步io操作需要在代码中显式调用,对于同步io操作则由io对象隐式调用(并不是run函数,不过也是等待io事件)。

可见,io_service提供的是一个生产者消费者模型。在异步io操作中需要我们手动控制消费者,调用run函数,它的基本工作模式如下:

  1. 等待io事件响应,如果所有io事件响应完成则退出
  2. 等待到io事件响应后,执行其对应的回调
  3. 继续等待下一个io事件,重复1-2

Io_servie 实现代码的基本类结构:

l Io_servie是接口类,为实现跨平台,采用了策略模式,所有接口均有impl_type实现。根据平台不同impl_type分为

n win_iocp_io_service Win版本的实现,这里主要分析Linux版本。

n task_io_service 非win平台下的实现,其代码结构为:

u detail/task_io_service_fwd.hpp 简单声明task_io_service名称

u detail/task_io_service.hpp 声明task_io_service的方法和属性

u detail/impl/task_io_service.ipp 具体实现文件

u 队列中的任务类型为opertioan,原型其实是typedef task_io_service_operation operation,其实现文件在detail/task_io_service_operation.hpp中,当队列中的任务被执行时,就是task_io_service_operation:: complete被调用的时候。

Io_servie::Post方法的实现

Post向队列中投递任务,然后激活空闲线程执行任务。其实现流程如下:

l Post接收handler作为参数,实际上是个仿函数,通过此仿函数构造出completion_handler对象,completion_handler继承自operation。然后调用post_immediate_completion。

l post_immediate_completion首先将outstanding_work_增加,然后调用post_deferred_completion。

l post_deferred_completion首先加锁将任务入列,然后调用wake_one_thread_and_unlock

l wake_one_thread_and_unlock尝试唤醒当前空闲的线程,其实现中特别之处在于,若没有空闲线程,但是有线程在执行task->run,即阻塞在epoll_wait上,那么先中断epoll_wait执行任务队列完成后再执行epoll_wait。

l first_idle_thread_维护了所有当前空闲线程,实际上使用了Leader/Follower模式,每次唤醒时只唤醒空闲线程的第一个。

Io_servie::run方法的实现

Run方法执行队列中的所有任务,直到任务执行完毕。

l run方法首先构造一个idle_thread_info,和first_idle_thread_类型相同,即通过first_idle_thread_将所有线程串联起来,它这个串联不是立即串联的,当该线程无任务可做是加入到first_idle_thread_的首部,有任务执行时,从first_idle_thread_中断开。这很正常,因为first_idle_thread_维护的是当前空闲线程。

l 加锁,循环执行do_one方法,直到do_one返回false

l do_one每次执行一个任务。首先检查队列是否为空,若空将此线程追加到first_idle_thread_的首部,然后阻塞在条件变量上,直到被唤醒。

l 当被唤醒或是首次执行,若stopped_为true(即此时stop方法被调用了),返回0

l 队列非空,pop出一个任务,检查队列无任务那么简单的解锁,若仍有,调用wake_one_thread_and_unlock尝试唤醒其他空闲线程执行。然后执行该任务,返回1.

l 实际上在执行队列任务时有一个特别的判断if (o == &task_operation_),那么将会执行task_->run,task_变量类型为reactor,在linux平台实现为epoll_reactor,实现代码文件为detail/impl/epoll_reactor.ipp,run方法实际上执行的是epoll_wait,run阻塞在epoll_wait上等待事件到来,并且处理完事件后将需要回调的函数push到io_servie的任务队列中,虽然epoll_wait是阻塞的,但是它提供了interrupt函数,该interrupt是如何实现的呢,它向epoll_wait添加一个文件描述符,该文件描述符中有8个字节可读,这个文件描述符是专用于中断epoll_wait的,他被封装到select_interrupter中,select_interrupter实际上实现是eventfd_select_interrupter,在构造的时候通过pipe系统调用创建两个文件描述符,然后预先通过write_fd写8个字节,这8个字节一直保留。在添加到epoll_wait中采用EPOLLET水平触发,这样,只要select_interrupter的读文件描述符添加到epoll_wait中,立即中断epoll_wait。很是巧妙。!!!实际上就是因为有了这个reactor,它才叫io_servie,否则就是一个纯的任务队列了。

l Run方法的原则是:

n 有任务立即执行任务,尽量使所有的线程一起执行任务

n 若没有任务,阻塞在epoll_wait上等待io事件

n 若有新任务到来,并且没有空闲线程,那么先中断epoll_wait,先执行任务

n 若队列中有任务,并且也需要epoll_wait监听事件,那么非阻塞调用epoll_wait(timeout字段设置为0),待任务执行完毕在阻塞在epoll_wait上。

n 几乎对线程的使用上达到了极致。

n 从这个函数中可以知道,在使用ASIO时,io_servie应该尽量多,这样可以使其epoll_wait占用的时间片最多,这样可以最大限度的响应IO事件,降低响应时延。但是每个io_servie::run占用一个线程,所以io_servie最佳应该和CPU的核数相同。

Io_servie::stop的实现

l 加锁,调用stop_all_threads

l 设置stopped_变量为true,遍历所有的空闲线程,依次唤醒

l task_interrupted_设置为true,调用task_的interrupt方法

l task_的类型为reactor,在run方法中已经做了分析

 

从中可以看出,io_service是一个工作队列的模型。在使用过程中一般有如下几个需要注意的地方:

1. run函数在io事件完成后会退出,导致后续基于该对象的异步io任务无法执行

由于io_service并不会主动常见调度线程,需要我们手动分配,常见的方式是给其分配一个线程,然后执行run函数。但run函数在io事件完成后会退出,线程会终止,后续基于该对象的异步io任务无法得到调度。

解决这个问题的方法是通过一个asio::io_service::work对象来守护io_service。这样,即使所有io任务都执行完成,也不会退出,继续等待新的io任务。

    boost::asio::io_service io;
    boost::asio::io_service::work work(io);
    io.run();

2. 回调在run函数的线程中同步执行,当回调处理时间较长时阻塞后续io响应

解决这个问题的方法有两种:1. 启动多线程执行run函数(run函数是线程安全的),2. 新启动一个线程(或通过线程池)来执行回调函数。一般来讲,如果回调处理事件不是特别短,应该使用在线程池中处理回调的方式。

3. 回调在run函数的线程中同步执行,io事件较多的时候得不到及时响应

这个其实是性能问题了,在多核cpu上可以通过在多个线程中执行run函数来解决这一问题。这种方式也只能充分利用cpu性能,本身性能问题就不是光靠软件就能解决的。

.net中的异步io调度方式

和io_service这种手动控制的方式比起来,.net则是纯粹的自动档了。IO调度由CLR托管了,无需手动控制。回调也是在线程池中执行,无需担心影响后续IO响应。

正是由于CLR的托管,在.net 的异步IO框架中,就没有类似io_service的调度对象存在,这也符合.net的一贯简洁做法。

 

◆boost::asio::io_service使用时的注意事项:

①请让boost::asio::io_service和boost::asio::io_service::work搭配使用。

②想让event按照进入(strand)时的顺序被执行,需要boost::asio::io_service要和boost::asio::io_service::strand搭配使用

③一般情况下,io_service的成员函数的使用顺序:

boost::asio::io_service构造,
boost::asio::io_service::run(),
boost::asio::io_service::stop(),
boost::asio::io_service::reset(),
boost::asio::io_service::run(),
......
boost::asio::io_service析构,

④不论有没有使用io_service::work,run()都会执行完io_service里面的event,(若没有用work,run就会退出)。
⑤一个新创建的io_service不需要执行reset()函数。
⑥在调用stop()后,在调用run()之前,请先调用reset()函数。
⑦函数stop()和reset()并不能清除掉io_service里面尚未执行的event。
我个人认为,也只有析构掉io_service,才能清空它里面的那些尚未执行的event了。(可以用智能指针)。

⑧函数stop(),stopped(),reset(),很简单,请单步调试,以明白它在函数里做了什么。

⑨boost的.hpp文件里面(一般情况下)有各个函数的使用说明,你可以随时查看。

◆下面是boost::asio::io_service的stop()和reset()函数的注释的翻译:

void boost::asio::io_service::stop();
BOOST_ASIO_DECL void stop();
/// Stop the io_service object's event processing loop.
/// 停止io_service对象的事件处理循环。
/**
 * This function does not block, but instead simply signals the io_service to
 * stop. All invocations of its run() or run_one() member functions should
 * return as soon as possible. Subsequent calls to run(), run_one(), poll()
 * or poll_one() will return immediately until reset() is called.
 */
 /**
 这个函数不阻塞,而是仅仅表示io_service停止了。
 它的run()或run_one()成员函数的调用应当尽快返回。
 对run()、run_one()、poll()、poll_one()的随后的调用将会立即返回直到reset()函数被调用了。
 */
void boost::asio::io_service::reset();
BOOST_ASIO_DECL void reset();
/// Reset the io_service in preparation for a subsequent run() invocation.
/// 重置io_service对象,为随后的run()调用做准备。
/**
 * This function must be called prior to any second or later set of
 * invocations of the run(), run_one(), poll() or poll_one() functions when a
 * previous invocation of these functions returned due to the io_service
 * being stopped or running out of work. After a call to reset(), the
 * io_service object's stopped() function will return @c false.
 *
 * This function must not be called while there are any unfinished calls to
 * the run(), run_one(), poll() or poll_one() functions.
 */
 /**
 io_service被停止,或者执行完handler而缺乏工作时,run()、run_one()、poll()、poll_one()函数的调用会被返回。
 这些函数在被调用之前,必须先调用reset函数。
 在reset函数被调用后,io_service对象的stopped函数将会返回false。
 当run()、run_one()、poll()、poll_one()函数的任何的调用未结束时,这个函数一定不能被调用。
 */

◆对stop()和reset()函数的一点说明(是我单步调试时看到的):

在Windows下,boost::asio::io_service类里面有一个数据成员为"stopped_"(Flag to indicate whether the event loop has been stopped.)。它是一个标志,它标志着事件循环是不是被stopped了。而boost::asio::io_service::reset()函数仅仅是赋值"stopped_=0"。boost::asio::io_service::stopped()函数仅仅是判断"0!=stopped_"的真假。你单步调试一下,就什么都知道了。

◆下面是我验证boost::asio::io_service的一个例子:

 1 #include <boost/asio.hpp>  
 2 #include <boost/thread.hpp>  
 3 #include <boost/atomic.hpp>  
 4 #include <boost/shared_ptr.hpp>  
 5 #include <boost/date_time/posix_time/ptime.hpp>  
 6 #include <boost/date_time.hpp>//boost::posix_time::to_iso_extended_string()需要此头文件。  
 7   
 8 //boost::atomic_bool coutFlag = false;  
 9 //error C2440: 'initializing' : cannot convert from 'bool' to 'boost::atomics::atomic<bool>'  
10 //故意写错,可以根据错误信息知道某类型的详细信息。  
11 boost::atomic_bool g_coutFlag(false);  
12 boost::atomic_int g_numIn(0);  
13 boost::atomic_int g_numOut(0);  
14   
15 boost::thread_group g_thgp;  
16 boost::asio::io_service g_io;  
17 boost::shared_ptr<boost::asio::io_service::work> g_pWork = \  
18 boost::shared_ptr<boost::asio::io_service::work>(new boost::asio::io_service::work(g_io));  
19 boost::asio::io_service::strand g_strand(g_io);  
20 std::vector<boost::posix_time::ptime> g_vecTimes;  
21   
22 void my_run_4_io_service(boost::asio::io_service& _io, int _idx)  
23 {  
24     _io.run();  
25     //想得到boost::asio::io_service::run()退出时的时刻,只能对io_service进行封装了。  
26     g_vecTimes[_idx] = boost::posix_time::microsec_clock::local_time();  
27 }  
28   
29 void outFun(int idx)  
30 {// io_service执行的handler。  
31     ++g_numOut;  
32     if (g_coutFlag.load())  
33         std::cout << "outFun: index=" << idx << std::endl;  
34     boost::this_thread::sleep_for(boost::chrono::milliseconds(500));  
35 }  
36   
37 void inFun()  
38 {  
39     for (int i = 1; i <= 10; ++i)  
40     {  
41         g_strand.post(boost::bind(outFun, i));  
42         ++g_numIn;  
43         boost::this_thread::sleep_for(boost::chrono::milliseconds(100));  
44     }  
45     g_coutFlag = true;  
46     g_io.stop();//调用它后,不论io_service有没有使用io_service::work类,各个线程的run()都会立即返回。  
47     g_vecTimes[0] = boost::posix_time::microsec_clock::local_time();  
48     int numDelta = g_numIn - g_numOut;  
49     std::cout << "inFun: numDelta=" << numDelta << std::endl;//还剩多少event没有被执行。  
50 }  
51   
52 int main()  
53 {  
54     int vecNum = 5;  
55     g_vecTimes.reserve(vecNum); g_vecTimes.resize(vecNum);  
56     //一个容纳 void fun(int i) 函数的 function对象。  
57     boost::function<void(int)> my_lambda_function_object = [vecNum](int secs)  
58     {  
59         boost::this_thread::sleep_for(boost::chrono::microseconds(1000 * 1000 * secs));  
60         std::cout << "now, time is " << boost::posix_time::  
61             to_iso_extended_string(boost::posix_time::microsec_clock::local_time()) << std::endl;  
62         for (int i = 0; i < vecNum; ++i)  
63             std::cout << i << " : " << boost::posix_time::to_iso_extended_string(g_vecTimes[i]) << std::endl;  
64     };  
65   
66     for (int i = 1; i < vecNum; ++i)  
67         g_thgp.create_thread(boost::bind(my_run_4_io_service, boost::ref(g_io), i));  
68     g_thgp.create_thread(inFun);  
69     //等待5秒,确保执行完毕我设计的那些操作。  
70     my_lambda_function_object(5);  
71     //析构掉io_service对应的io_service::work对象,此时io_service里面还有event。  
72     g_pWork = nullptr;  
73     boost::this_thread::sleep_for(boost::chrono::milliseconds(1000 * 1));  
74     g_io.reset();  
75     boost::this_thread::sleep_for(boost::chrono::seconds(1));  
76     //因为work被析构掉了,所以启动的那些线程在执行完event后,都自行退出了。  
77     for (int i = 1; i < vecNum; ++i)  
78         g_thgp.create_thread(boost::bind(my_run_4_io_service, boost::ref(g_io), i));  
79     //等待6秒,确保io_service中剩余的event被执行完毕。  
80     my_lambda_function_object(6);  
81     std::cout << "done." << std::endl;  
82     int cmd_val = getchar();  
83     return 0;  
84 }  

 

boost::asio::io_service创建线程池简单实例

https://blog.csdn.net/guotianqing/article/details/100730340

简介


boost::asio提供了一个跨平台的异步编程IO模型库,io_service类在多线程编程模型中提供了任务队列和任务分发功能。

io_service最常用的接口是:run, post, stop。

本文简要介绍io_service的使用,详细内容可以参阅相关reference。

启动一个线程


使用run()启动。

run()会阻塞,直到:

  • 所有的任务已经完成并且没有任务需要分发处理
  • 或者调用了stop()
启动任务

简单测试例程如下:

#include <boost/asio/io_service.hpp>
#include <iostream>

int main(int argc, char *argv[])
{
		// 创建io_service
    boost::asio::io_service io_service;
    // 附加任务
    boost::asio::io_service::work work(io_service);
		// 启动并阻塞
    io_service.run();
    
    std::cout << "Yes, run() is returned!" << std::endl;

    return 0;
}

注意,程序打印文本,也就是说io_service阻塞在了run()。我们使用了work类,它会使得run()一直在任务待执行而不会退出,如果注释掉这一行,可以看到run会立即执行完毕并打印文本。

结束任务

使用work时会阻塞run,可以使用work类型的指针来结束所有任务。

boost::asio::io_service io_service;
boost::shared_ptr< boost::asio::io_service::work > work(new boost::asio::io_service::work( io_service ));

// 结束任务
work.reset();
// 立即返回
io_service.run();

启动线程池


在多个线程中调用run()即可开启线程池,io_service负责执行任务处理。所有在线程池中等待的线程是平等的,io_service会随机选择一个线程去执行任务。

使用thread库

这里使用了boost::thread库,例子如下:

#include <boost/asio/io_service.hpp>
#include <boost/thread/thread.hpp>
#include <iostream>

boost::asio::io_service io_service;

void WorkerThread()
{
	std::cout << "Thread Start\n";
	io_service.run();
	std::cout << "Thread Finish\n";
}

int main( int argc, char * argv[] )
{
	boost::shared_ptr< boost::asio::io_service::work > work(new boost::asio::io_service::work( io_service ));

	std::cout << "Press [return] to exit." << std::endl;

	boost::thread_group worker_threads;
	for( int x = 0; x < 4; ++x )
	{
		worker_threads.create_thread( WorkerThread );
	}

	std::cin.get();

	io_service.stop();

	worker_threads.join_all();

	return 0;
}

stop()会告知io_service,所有的任务需要终止。它的调用可能会使已经进入队列的任务得不到执行。

如果想让它们都执行,最好先销毁work对象。如果想让系统马上停止,则调用stop()。

使用bind

使用boost::bind可以把函数调用包装成为对象。

调用常规函数的示例:

#include <boost/bind.hpp>
#include <iostream>

void F2( int i, float f )
{
	std::cout << "i: " << i << std::endl;
	std::cout << "f: " << f << std::endl;
}

int main( int argc, char * argv[] )
{
	boost::bind( &F2, 42, 3.14f )();// 最后的括号表明调用该函数,如果去掉括号表示仅封装对象
	return 0;
}

调用类成员函数示例:

class MyClass
{
public:
	void F3( int i, float f )
	{
		std::cout << "i: " << i << std::endl;
		std::cout << "f: " << f << std::endl;
	}
};

int main( int argc, char * argv[] )
{
	MyClass c;
	boost::bind( &MyClass::F3, &c, 42, 3.14f )();
	return 0;
}

回到io_service,使用bind可以改写程序:

#include <boost/asio/io_service.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )
{
	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl;
	global_stream_lock.unlock();

	io_service->run();

	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() <<	"] Thread Finish" << std::endl;
	global_stream_lock.unlock();
}

int main( int argc, char * argv[] )
{
	boost::shared_ptr< boost::asio::io_service > io_service(new boost::asio::io_service);
	boost::shared_ptr< boost::asio::io_service::work > work(new boost::asio::io_service::work( *io_service ));

	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Press [return] to exit." << std::endl;
	global_stream_lock.unlock();

	boost::thread_group worker_threads;
	for( int x = 0; x < 4; ++x )
	{
		worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );
	}

	std::cin.get();

	io_service->stop();

	worker_threads.join_all();

	return 0;
}
post任务

至此,启动了线程,其实并没有给线程做任务。也就是说,启动的线程处于空闲状态。

可以使用post/dispatch分发任务,它们的区别是:

  • 如果可以,dispatch会立即执行任务,否则把任务加入到queue
  • post只会把任务加入到队列

具体使用何种方式需要根据具体情况确定。

如下的程序打印数字:

boost::mutex global_stream_lock;

void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )
{
	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl;
	global_stream_lock.unlock();

	io_service->run();

	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Thread Finish" << std::endl;
	global_stream_lock.unlock();
}

void Dispatch( int x )
{
	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()  << "] " << __FUNCTION__  << " x = " << x <<  std::endl;
	global_stream_lock.unlock();
}

void Post( int x )
{
	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()  << "] " << __FUNCTION__  << " x = " << x <<  std::endl;
	global_stream_lock.unlock();
}

void Run3( boost::shared_ptr< boost::asio::io_service > io_service )
{
	for( int x = 0; x < 3; ++x )
	{
		io_service->dispatch( boost::bind( &Dispatch, x * 2 ) );
		io_service->post( boost::bind( &Post, x * 2 + 1 ) );
		boost::this_thread::sleep( boost::posix_time::milliseconds( 1000 ) );
	}
}

int main( int argc, char * argv[] )
{
	boost::shared_ptr< boost::asio::io_service > io_service( new boost::asio::io_service);
	boost::shared_ptr< boost::asio::io_service::work > work(new boost::asio::io_service::work( *io_service ));

	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id() << "] The program will exit when all  work has finished." <<  std::endl;
	global_stream_lock.unlock();

	boost::thread_group worker_threads;
	for( int x = 0; x < 1; ++x )
	{
		worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );
	}

	io_service->post( boost::bind( &Run3, io_service ) );

	work.reset();

	worker_threads.join_all();

	return 0;
}

可以看到,所有的任务得到了执行,但执行顺序可能不同。这个示例也可以看出混合使用post和dispatch可能存在的问题。

strand顺序处理

strand提供顺序化的事件执行器。意思是,如果以“work1->work2->work3”的顺序post,不管有多少个工作线程,它们依然会以这样的顺序执行任务。

#include <boost/asio/io_service.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio/strand.hpp>
#include <iostream>

boost::mutex global_stream_lock;

void WorkerThread( boost::shared_ptr< boost::asio::io_service > io_service )
{
	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Thread Start" << std::endl;
	global_stream_lock.unlock();

	io_service->run();

	global_stream_lock.lock();
	std::cout << "[" << boost::this_thread::get_id() << "] Thread Finish" << std::endl;
	global_stream_lock.unlock();
}

void PrintNum( int x )
{
	std::cout << "[" << boost::this_thread::get_id() << "] x: " << x << std::endl;
}

int main( int argc, char * argv[] )
{
	boost::shared_ptr< boost::asio::io_service > io_service( new boost::asio::io_service);
	boost::shared_ptr< boost::asio::io_service::work > work(new boost::asio::io_service::work( *io_service ));
	boost::asio::io_service::strand strand( *io_service );

	global_stream_lock.lock();
	std::cout << "[" <<  boost::this_thread::get_id()  << "] The program will exit when all  work has finished." <<  std::endl;
	global_stream_lock.unlock();

	boost::thread_group worker_threads;
	for( int x = 0; x < 2; ++x )
	{
		worker_threads.create_thread( boost::bind( &WorkerThread, io_service ) );
	}

	boost::this_thread::sleep( boost::posix_time::milliseconds( 1000 ) );

	//strand.post( boost::bind( &PrintNum, 1 ) );
	//strand.post( boost::bind( &PrintNum, 2 ) );
	//strand.post( boost::bind( &PrintNum, 3 ) );
	//strand.post( boost::bind( &PrintNum, 4 ) );
	//strand.post( boost::bind( &PrintNum, 5 ) );

	io_service->post( boost::bind( &PrintNum, 1 ) );
	io_service->post( boost::bind( &PrintNum, 2 ) );
	io_service->post( boost::bind( &PrintNum, 3 ) );
	io_service->post( boost::bind( &PrintNum, 4 ) );
	io_service->post( boost::bind( &PrintNum, 5 ) );

	work.reset();

	worker_threads.join_all();

	return 0;
}

去掉了打印数据时的锁。如果使用post,会得到比较混乱的输出,而如果使用strand,则能得到清晰的输出,这就是strand!

小结


总之,使用io_service能够方便地构建多线程处理程序。它的总体使用流程总结如下:

#include <boost/asio/io_service.hpp>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>

/*
 * Create an asio::io_service and a thread_group (through pool in essence)
 */
boost::asio::io_service ioService;
boost::thread_group threadpool;


/*
 * This will start the ioService processing loop. All tasks 
 * assigned with ioService.post() will start executing. 
 */
boost::asio::io_service::work work(ioService);

/*
 * This will add 2 threads to the thread pool. (You could just put it in a for loop)
 */
threadpool.create_thread(
    boost::bind(&boost::asio::io_service::run, &ioService)
);
threadpool.create_thread(
    boost::bind(&boost::asio::io_service::run, &ioService)
);

/*
 * This will assign tasks to the thread pool. 
 * More about boost::bind: "http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html#with_functions"
 * You can use strand when necessary, if so, remember add "strand.h"
 */
ioService.post(boost::bind(myTask, "Hello World!"));
ioService.post(boost::bind(clearCache, "./cache"));
ioService.post(boost::bind(getSocialUpdates, "twitter,gmail,facebook,tumblr,reddit"));

/*
 * This will stop the ioService processing loop. Any tasks
 * you add behind this point will not execute.
*/
ioService.stop();

/*
 * Will wait till all the threads in the thread pool are finished with 
 * their assigned tasks and 'join' them. Just assume the threads inside
 * the threadpool will be destroyed by this method.
 */
threadpool.join_all();

参考资料:


A guide to getting started with boost::asio
浅谈 Boost.Asio 的多线程模型
How to create a thread pool using boost in C++?

posted @ 2021-10-12 15:02  CharyGao  阅读(87)  评论(0)    收藏  举报