[转] 半同步半异步I/O的设计模式(half sync/half async)

http://blog.csdn.net/cjfeii/article/details/17267487

半同步半异步I/O的设计模式(half sync/half async)


1.动机:

众所周知,同步模式编程简单,但是I/O的利用利率低;而异步模式编程复杂,但是I/O利用率高。
综合同步异步的有优点,就有了半同步半异步的设计模式。

这个模式中,高层使用同步I/O模型,简化编程。低层使用异步I/O模型,高效执行。

half sync/half async可以很好的使得"变成复杂度"和"执行效率"之间达到一种平衡.


2.应用场景:

半同步半异步模式在下面的场景中使用:

2.1 一个系统中的进程有下面的特征:

系统必须响应和处理外部异步发生的事件,
如果为每一个外部资源的事件分派一个独立的线程同步处理I/O,效率很低。
如果上层的任务以同步方式处理I/O,实现起来简单。

2.2 一个或多个任务必须在单独的控制线程中执行,其它任务可以在多线程中执行:

上层的任务(如:数据库查询,文件传输)使用同步I/O模型,简化了编写并行程序的难度。
而底层的任务(如网络控制器的中断处理)使用异步I/O模型,提供了执行效率。

一般情况下,上层的任务要比下层的任务多,使用一个简单的层次实现异步处理的复杂性,可以对外隐藏异步处理的细节。另外,同步层次和异步层次任务间的通信使用一个队列来协调。


3.实现方案:

可以分为三层:同步任务层,队列层,异步任务层。

3.1 同步任务层(用户级的进程):

本层的任务完成上层的I/O操作,使用同步I/O模型,通过队列层的队列中传输数据。和异步层不同,同步层的任务使用活动对象执行,这些活动对象有自己运行栈和寄存器状态。当执行同步I/O的时候,他们会被阻塞/睡眠。

3.2 队列层:

这个层在同步任务层和异步任务层之间,提供了同步控制和缓存的功能。异步任务的I/O 事件被缓存到消息队列中,同步任务层在队列中提取这些事件(相反方向亦然)

3.3 异步任务层:

处理低层的事件,这些事件由多个外部的事件源产生(例如网卡,终端)。和异步任务不同,此层的实体是被动对象,没有自己的运行栈,要求不能被阻塞。


4.优缺点:

4.1 半同步半异步模式有下面的优点:

上层的任务被简化
不同层可以使用不同的同步策略
层间的通信被限制在单独的一点,因为所有的交互使用队列层协调。
在多处理器环境中提高了性能。

4.2 半同步半异步模式有下面的缺点:

跨边界导致的性能消耗,这是因为同步控制,数据拷贝和上下文切换会过度地消耗资源。

上层任务缺少异步I/O的实现。


5.一个样例:

  1. #include <pthread.h>  // for pthread.  
  2. #include <semaphore.h>    // for sem.  
  3. #include <queue>      // for queue.  
  4. #include <vector>     // for vector.  
  5. #include <string.h>       // for memset.  
  6.   
  7. // for file operation.  
  8. #include <sys/types.h>  
  9. #include <sys/stat.h>  
  10. #include <fcntl.h>  
  11. #include <unistd.h>  
  12.   
  13. // for print log.  
  14. #include <iostream>  
  15.   
  16. using namespace std;  
  17.   
  18. // 队列层  
  19. class TaskQueue  
  20. {  
  21.     public:  
  22.         /* 
  23.          * 定义item。 
  24.          */  
  25.         typedef struct _taskItem  
  26.         {  
  27.             int start_offset;  
  28.             int size;  
  29.             void *buffer;  
  30.             sem_t *sem;  
  31.         } TaskItem;  
  32.     public:  
  33.         TaskQueue()  
  34.         {  
  35.             pthread_mutex_init(&m_mutex, NULL);  
  36.         }  
  37.         ~TaskQueue()  
  38.         {  
  39.             pthread_mutex_destroy(&m_mutex);  
  40.         }  
  41.   
  42.         /* 
  43.          * 添加任务。 
  44.          */  
  45.         void append(TaskItem &item)  
  46.         {  
  47.             pthread_mutex_lock(&m_mutex);  
  48.             m_items.push(item);  
  49.             pthread_mutex_unlock(&m_mutex);  
  50.         }  
  51.   
  52.         /* 
  53.          * 抛出一个任务; 
  54.          */  
  55.         bool pop(TaskItem &item)  
  56.         {  
  57.             pthread_mutex_lock(&m_mutex);  
  58.             if(m_items.empty())  
  59.             {  
  60.                 pthread_mutex_unlock(&m_mutex);  
  61.                 return false;  
  62.             }  
  63.             item.start_offset = m_items.front().start_offset;  
  64.             item.size = m_items.front().size;  
  65.             item.buffer = m_items.front().buffer;  
  66.             item.sem = m_items.front().sem;  
  67.             m_items.pop();  
  68.             pthread_mutex_unlock(&m_mutex);  
  69.   
  70.             return true;  
  71.         }  
  72.   
  73.         /* 
  74.          * 获取item的size; 
  75.          */  
  76.         int getSize()  
  77.         {  
  78.             int size = 0;  
  79.             pthread_mutex_lock(&m_mutex);  
  80.             size  = m_items.size();  
  81.             pthread_mutex_unlock(&m_mutex);  
  82.             return m_items.size();  
  83.         }  
  84.   
  85.     private:  
  86.         queue<TaskItem> m_items;  // 任务队列。  
  87.         pthread_mutex_t m_mutex;    // 任务队列的互斥量。  
  88. };  
  89.   
  90. // 异步任务层  
  91. class AioProcessor  
  92. {  
  93.     public:  
  94.         AioProcessor(int fd, TaskQueue *queue)  
  95.         {  
  96.             m_fd = fd;  
  97.             m_queue = queue;  
  98.             m_isStartup = false;  
  99.         }  
  100.         ~AioProcessor()  
  101.         {  
  102.             shutdwon();  
  103.         }  
  104.   
  105.         void startup(int thread_count)  
  106.         {  
  107.             if(!m_isStartup)  
  108.             {  
  109.                 /* 
  110.                  * 启动指定数目的线程。 
  111.                  */  
  112.                 m_isStartup = true;  
  113.                 for(int i=0; i<thread_count; ++i)  
  114.                 {  
  115.                     pthread_t tid;  
  116.                     pthread_create(&tid, NULL, process, this);  
  117.                     m_tids.push_back(tid);  
  118.                 }  
  119.             }  
  120.         }  
  121.         void shutdwon()  
  122.         {  
  123.             if(m_isStartup)  
  124.             {  
  125.                 /* 
  126.                  * 结束启动的线程。 
  127.                  */  
  128.                 m_isStartup = false;  
  129.                 vector<pthread_t>::iterator iter = m_tids.begin();  
  130.                 for(; iter<m_tids.end(); ++iter)  
  131.                 {  
  132.                     pthread_join(*iter, NULL);  
  133.                 }  
  134.             }  
  135.         }  
  136.   
  137.     private:  
  138.         static void *process(void *param)  
  139.         {  
  140.             AioProcessor *processor = (AioProcessor *)param;  
  141.   
  142.             /* 
  143.              * 处理读文件请求,读取完毕发送完毕信号。 
  144.              */  
  145.             TaskQueue::TaskItem item_to_be_process;  
  146.             while(processor->m_isStartup)  
  147.             {  
  148.                 if(processor->m_queue->pop(item_to_be_process))  
  149.                 {  
  150.                     pread(processor->m_fd, item_to_be_process.buffer, item_to_be_process.size, item_to_be_process.start_offset);  
  151.                     sem_post(item_to_be_process.sem);  
  152.                 }  
  153.                 else  
  154.                 {  
  155.                     usleep(1000);  
  156.                 }  
  157.             }  
  158.   
  159.             /* 
  160.              * 线程结束。 
  161.              */  
  162.             return NULL;  
  163.         }  
  164.     private:  
  165.         int m_fd;                   // 文件句柄  
  166.         TaskQueue *m_queue;         // 任务队列  
  167.         vector<pthread_t> m_tids; // 线程Id  
  168.         bool m_isStartup;           // true:已启动;false:未启动。  
  169. };  
  170.   
  171. // 应用层  
  172. class Reader  
  173. {  
  174.     public:  
  175.         Reader(int fd)  
  176.             : m_fd(fd), m_queue(), m_processor(new AioProcessor(m_fd, &m_queue))  
  177.         {  
  178.             /* 
  179.              * 启动processor。 
  180.              */  
  181.             m_processor->startup(2);  
  182.         }  
  183.   
  184.         ~Reader()  
  185.         {  
  186.             /* 
  187.              * 停止processor,并释放相关资源。 
  188.              */  
  189.             m_processor->shutdwon();  
  190.             delete m_processor;  
  191.         }  
  192.         void read(const int start_offset, int size, void *buffer)  
  193.         {  
  194.             /* 
  195.              * 构造一个item。 
  196.              */  
  197.             sem_t sem;  
  198.             sem_init(&sem, 0, 0);  
  199.             TaskQueue::TaskItem item;  
  200.             item.start_offset = start_offset;  
  201.             item.size = size;  
  202.             item.buffer = buffer;  
  203.             item.sem = &sem;  
  204.   
  205.             /* 
  206.              * 将上面的item加入到任务队列中。 
  207.              */  
  208.             m_queue.append(item);  
  209.   
  210.             /* 
  211.              * 等待读文件操作完成。 
  212.              */  
  213.             sem_wait(&sem);  
  214.             sem_destroy(&sem);  
  215.   
  216.             /* 
  217.              * 读文件操作完成。 
  218.              */  
  219.             return;  
  220.   
  221.         }  
  222.     private:  
  223.         int             m_fd;           // 文件句柄.  
  224.         TaskQueue       m_queue;        // 任务队列.  
  225.         AioProcessor*   m_processor;    // 任务处理器.  
  226. };  
  227.   
  228. // 测试样例。  
  229. int main()  
  230. {  
  231.     int fd = open("./a.file", O_RDONLY);  
  232.     Reader reader(fd);  
  233.   
  234.     int size = 10;  
  235.     char *buf = new char[size + 1];  
  236.     memset(buf, '\0', size+1);  
  237.     for(int i=0; i<10; i++)  
  238.     {  
  239.         reader.read(i*size, size, buf);  
  240.         cout<<buf<<endl;  
  241.     }  
  242.   
  243.     close(fd);  
  244.     return 0;  
  245. }  

注:
上述代码比较简单,有很多地方需要改进,比如:
1.以上代码的编译环境是:RedHat6.3(g++ (GCC) 4.5.2)。
2.pthread_mutex_init,pthread_create,open,pread等,都需判断返回值。
3.AioProcessor一个线程一次处理一个read request,不是很高效,可能使得queue中的锁成为瓶颈。
4.肯定还有其他的问题,欢迎提出宝贵意见……

6.参考:

hs-ha中文版本

posted @ 2015-05-11 14:07  枪侠  阅读(235)  评论(0编辑  收藏  举报