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

半同步半异步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.一个样例:

#include <pthread.h>	// for pthread.
#include <semaphore.h>	// for sem.
#include <queue>		// for queue.
#include <vector>		// for vector.
#include <string.h>		// for memset.

// for file operation.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

// for print log.
#include <iostream>

using namespace std;

// 队列层
class TaskQueue
{
	public:
		/*
		 * 定义item。
		 */
		typedef struct _taskItem
		{
			int start_offset;
			int size;
			void *buffer;
			sem_t *sem;
		} TaskItem;
	public:
		TaskQueue()
		{
			pthread_mutex_init(&m_mutex, NULL);
		}
		~TaskQueue()
		{
			pthread_mutex_destroy(&m_mutex);
		}

		/*
		 * 添加任务。
		 */
		void append(TaskItem &item)
		{
			pthread_mutex_lock(&m_mutex);
			m_items.push(item);
			pthread_mutex_unlock(&m_mutex);
		}

		/*
		 * 抛出一个任务;
		 */
		bool pop(TaskItem &item)
		{
			pthread_mutex_lock(&m_mutex);
			if(m_items.empty())
			{
				pthread_mutex_unlock(&m_mutex);
				return false;
			}
			item.start_offset = m_items.front().start_offset;
			item.size = m_items.front().size;
			item.buffer = m_items.front().buffer;
			item.sem = m_items.front().sem;
			m_items.pop();
			pthread_mutex_unlock(&m_mutex);

			return true;
		}

		/*
		 * 获取item的size;
		 */
		int getSize()
		{
			int size = 0;
			pthread_mutex_lock(&m_mutex);
			size  = m_items.size();
			pthread_mutex_unlock(&m_mutex);
			return m_items.size();
		}

	private:
		queue<TaskItem> m_items;	// 任务队列。
		pthread_mutex_t m_mutex;	// 任务队列的互斥量。
};

// 异步任务层
class AioProcessor
{
	public:
		AioProcessor(int fd, TaskQueue *queue)
		{
			m_fd = fd;
			m_queue = queue;
			m_isStartup = false;
		}
		~AioProcessor()
		{
			shutdwon();
		}

		void startup(int thread_count)
		{
			if(!m_isStartup)
			{
				/*
		 		 * 启动指定数目的线程。
		 		 */
				m_isStartup = true;
				for(int i=0; i<thread_count; ++i)
				{
					pthread_t tid;
					pthread_create(&tid, NULL, process, this);
					m_tids.push_back(tid);
				}
			}
		}
		void shutdwon()
		{
			if(m_isStartup)
			{
				/*
				 * 结束启动的线程。
				 */
				m_isStartup = false;
				vector<pthread_t>::iterator iter = m_tids.begin();
				for(; iter<m_tids.end(); ++iter)
				{
					pthread_join(*iter, NULL);
				}
			}
		}

	private:
		static void *process(void *param)
		{
			AioProcessor *processor = (AioProcessor *)param;

			/*
			 * 处理读文件请求,读取完毕发送完毕信号。
			 */
			TaskQueue::TaskItem item_to_be_process;
			while(processor->m_isStartup)
			{
				if(processor->m_queue->pop(item_to_be_process))
				{
					pread(processor->m_fd, item_to_be_process.buffer, item_to_be_process.size, item_to_be_process.start_offset);
					sem_post(item_to_be_process.sem);
				}
				else
				{
					usleep(1000);
				}
			}

			/*
			 * 线程结束。
			 */
			return NULL;
		}
	private:
		int m_fd;					// 文件句柄
		TaskQueue *m_queue;			// 任务队列
		vector<pthread_t> m_tids;	// 线程Id
		bool m_isStartup;			// true:已启动;false:未启动。
};

// 应用层
class Reader
{
	public:
		Reader(int fd)
			: m_fd(fd), m_queue(), m_processor(new AioProcessor(m_fd, &m_queue))
		{
			/*
			 * 启动processor。
			 */
			m_processor->startup(2);
		}

		~Reader()
		{
			/*
			 * 停止processor,并释放相关资源。
			 */
			m_processor->shutdwon();
			delete m_processor;
		}
		void read(const int start_offset, int size, void *buffer)
		{
			/*
			 * 构造一个item。
			 */
			sem_t sem;
			sem_init(&sem, 0, 0);
			TaskQueue::TaskItem item;
			item.start_offset = start_offset;
			item.size = size;
			item.buffer = buffer;
			item.sem = &sem;

			/*
			 * 将上面的item加入到任务队列中。
			 */
			m_queue.append(item);

			/*
			 * 等待读文件操作完成。
			 */
			sem_wait(&sem);
			sem_destroy(&sem);

			/*
			 * 读文件操作完成。
			 */
			return;

		}
	private:
		int 			m_fd;			// 文件句柄.
		TaskQueue		m_queue;		// 任务队列.
		AioProcessor*	m_processor;	// 任务处理器.
};

// 测试样例。
int main()
{
	int fd = open("./a.file", O_RDONLY);
	Reader reader(fd);

	int size = 10;
	char *buf = new char[size + 1];
	memset(buf, '\0', size+1);
	for(int i=0; i<10; i++)
	{
		reader.read(i*size, size, buf);
		cout<<buf<<endl;
	}

	close(fd);
	return 0;
}

注:
上述代码比较简单,有很多地方需要改进,比如:
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.pdf
hs-ha中文版本

你可能感兴趣的:(设计模式,linux,IO,async,half,半同步半异步,synchalf)