【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
和多线程相比较,多进程最大的好处就是安全。一个进程挂了,不影响其他进程的运行。但是多线程也有自己的优点,那就是数据共享和消息传递会比较快。比如说,如果一个消息需要分发到不同的插件里面,这个时候在多线程下面就特别好实现。一般这种情况下,我们会创建若干个线程,不同的业务模块当成plugin插入进来,如果哪个plugin有消息需要处理,那么就从线程池找到一个线程,bind到特定的plugin来处理。处理完了,如果有其他的plugin需要处理,就继续处理其他的plugin。
当这个plugin有bug的时候,还可以卸载、重新加载,毕竟是以显式动态库的形式加载的。这就是多线程的优势。这个时候线程的接收和发送,也可以做成一个插件。我们这里为了简便,用一个int数据来代替,实际使用的使用会稍微复杂一点。
1、初始化空间
这里采用了静态空间来处理数据。另外有三个量比较关键,分别是startPos、endPos和cnt。其中当startPos=endPos的时候,只有两种情况,要么一个数据没有,要么数据已经存满。所以这种情况下,需要一个cnt来判别一下。
#include <iostream>
#include <cassert>
using namespace std;
// share memory access method
#define MAX_NUM 256
static int dataBuf[MAX_NUM];
static int startPos = 0;
static int endPos = 0; // ebdPos = startPos only when empty or full
static int cnt = 0; // very important
2、使用锁
对于数据的创建和保存,一般存在并发的可能性,所以这个时候,需要一个添加锁、释放锁的操作。这里使用了空函数,大家可以根据自己的使用情况补充完整。
static void lock() // for lock method
{
return;
}
static void unlock() // for unlock method
{
return;
}
3、存储数据
当我们收到一个消息,需要群发到其他模块的时候,就会调用这个函数,
bool putData(int data) // put data here
{
lock();
if (cnt == MAX_NUM)
{
unlock();
return false;
}
dataBuf[startPos] = data;
startPos = (startPos + 1) % MAX_NUM;
cnt += 1;
unlock();
return true;
}
4、获取数据
获取数据的动作对于plugin来说,一般都是不可见的。更常见的做法是,把接收到的消息和特定的函数做一个bind,这样消息来了之后直接调用对应的函数就可以了。
bool getData(int& data) // get data here
{
lock();
if (cnt == 0)
{
unlock();
return false;
}
data = dataBuf[endPos];
endPos = (endPos + 1) % MAX_NUM;
cnt -= 1;
unlock();
return true;
}
5、测试和验证
编写好了对应的函数,最好自己写一个单元测试确认一下,这样也比较放心一点。这有点类似于单元测试的编写,其实这是非常好的开发方法,只不过大家平时要么不太重视,要么因为工作量的原因,真正做的同学少之又少。
int main(int argc, char* argv[]) // function starts here
{
int value = 10;
assert(putData(value) == true);
assert(startPos = 1);
assert(cnt == 1);
value = 0;
assert(getData(value) == true);
assert(value == 10);
assert(endPos == 1);
assert(cnt == 0);
startPos = MAX_NUM - 1;
endPos = MAX_NUM - 1;
value = 100;
assert(putData(value) == true);
assert(startPos == 0);
assert(cnt == 1);
value = 0;
assert(getData(value) == true);
assert(value == 100);
assert(endPos == 0);
assert(cnt == 0);
return 0;
}
6、拓展和补充
上面暂时只是int数据的压入和保存,如果是char数据的操作,会稍微复杂一点,因为有可能会超过内存可能会超过界限往回写,这样需要写的时候注意下。
#include <iostream>
#include <cstring>
using namespace std;
#define MAX_BUF_SIZE 4096
char dataBuf[MAX_BUF_SIZE];
int startPos = 0;
int endPos = 0;
int cnt = 0;
bool putData(char* pBuf)
{
int size = *(int*)pBuf;
if ((MAX_BUF_SIZE - cnt) < size)
{
return false;
}
if ((MAX_BUF_SIZE - startPos) > size)
{
memmove((void*)&dataBuf[startPos], pBuf, size);
startPos += size;
cnt += size;
return true;
}
memmove((void*)&dataBuf[startPos], pBuf, MAX_BUF_SIZE - startPos);
memmove((void*)&dataBuf[0], &pBuf[MAX_BUF_SIZE - startPos], size - (MAX_BUF_SIZE - startPos));
startPos = size - (MAX_BUF_SIZE - startPos);
cnt += size;
return true;
}
bool getData(char* pBuf, int& size)
{
char data[4];
if (cnt == 0)
{
return false;
}
// get current size
if (endPos <= (MAX_BUF_SIZE - 4))
{
size = *(int*)&dataBuf[endPos];
}
else
{
if (endPos == (MAX_BUF_SIZE - 3))
{
data[0] = dataBuf[endPos];
data[1] = dataBuf[endPos + 1];
data[2] = dataBuf[endPos + 2];
data[3] = dataBuf[0];
}
else if (endPos == (MAX_BUF_SIZE - 2))
{
data[0] = dataBuf[endPos];
data[1] = dataBuf[endPos + 1];
data[2] = dataBuf[0];
data[3] = dataBuf[1];
}
else
{
data[0] = dataBuf[endPos];
data[1] = dataBuf[0];
data[2] = dataBuf[1];
data[3] = dataBuf[2];
}
size = *(int*)&data[0];
}
if ((MAX_BUF_SIZE - endPos) > size)
{
memmove(pBuf, (void*)&dataBuf[endPos], size);
endPos += size;
cnt -= size;
return true;
}
memmove(pBuf, (void*)&dataBuf[endPos], MAX_BUF_SIZE - endPos);
memmove(&pBuf[MAX_BUF_SIZE - endPos], (void*)&dataBuf[0], size - (MAX_BUF_SIZE - endPos));
endPos = size - (MAX_BUF_SIZE - endPos);
cnt -= size;
return true;
}