本发明涉及一种多线程数据传输方法,特别是涉及一种适用于多线程通信中,多线程访问的多线程数据传输方法。
背景技术:
一般在多线程通信中,会存在一些对临界资源(多个线程都可能同时被访问)的访问,因临界资源是公共的,如果同时被多个线程读写的时候,临界资源会成为一种未知状态,是不安全的,会导致程序产生莫名的错误,所以程序在使用临界资源时往往会通过线程锁来保证临界资源的访问是安全可控,通过线程锁来保证有任意时刻只有会一个线程来操作临界资源;但是这种机制会导致其它线程处理等待状态,从而降低系统处理性能。
技术实现要素:
本发明要解决的技术问题是提供一种多线程数据传输方法,能够解决两线程之间数据传输,即对队列容器这个临界资源实现无锁化的访问,极大提高性能。
本发明采用的技术方案如下:一种多线程数据传输方法,具体方法为:
初始化系统时,申请一个能够容纳N个指针的连续内存空间;所述N=2m,m为大于等于1的整数;每个指针位置为一个单位,N个指针位置组成一个数组空间dataPtrArray;所述指针指向用户数据地址;
定义两个整数值:writePos和readPos;其中,writePos表示数据写入线程当前正要写入所述数组空间的位置;readPos表示数据读取线程当前正要从所述数组空间读取数据的位置;数据写入成功,则writePos值自增;数据读取成功,则readPos值自增,将被成功读取的数组空间位置的指针值清零,清零后,写入线程再发现这个位置是零,才能够正常写入新的用户指针数据;
当dataPtrArray写满数据但是并未被及时读取走时,会写入失败;当dataPtrArray中所有值都为零时,会读取失败;
定义一个整数掩码PosMask,其值为N-1;通过PosMask的值与读写位置的值做与运算,来决定当前正要写入和读取的真正的数组空间的位置,即当前写入的位置应该是dataPtrArray[writePos&PosMask],读取的位置是dataPtrArray[readPos&PosMask]。
申明成连续的内存数组,能够快速的通过数组的单位序列值快速的读取和写入数据指针;但是当数据写入和读取到数组尾部时,势必要通过程序的判断,并且此判断在多线程中会引起性能开销,固本发明使用了一种能够无性能开销,并且数据在读取和写入数据尾部数据后,直接跳到数据首部;通过PosMask的值与读写位置的值做与运算,来决定当前正要写入和读取的真正在数据组空间的位置数据指针在dataPtrArray读取实现了尾头相接,且无任何开锁,具有极高的性能。
所述方法还包括:写入数据时,采用Intel的锁总线技术,以原子的方式,同时判断写入位置是否为空,若为空,则写入,若不为空,则表示队列已经写满了,此时写入线程陷入等待;读取数据时,采用Intel的锁总线技术,以原子方式的判断读取位置是否为空,若不为空,则读取返回,并且将读取位置清零,若为空,则表示整个队列都已经读取完,此时读取线程陷入等待;当成功的写入一个数据指针时通知读取线程数据可读取,此时,如果读取线程为休眠状态,则将被唤醒;当成功的读取一个数据指针时通知写入线程数据可写入,此时,如果写入线程为休眠状态,则将被唤醒。
因为读取和写入线程是异步的,所以readPos和writePos可能存在重叠的情况(即当数据空间被写满或者为空时),此时读取和写入是同一个位置,即是一个临界区,这种情况必须使数据得到保护,相较传统的线程锁具有很高的代价,本方法以一种性能极高的方式实现了临界区的高效读写。
所述内存空间中,以每8个字节为一个单位。内存空间以8个字节,即一个uint64_t为一个单位,可以存放任意系统下的sizeof(void*)的值,即存储一个指针的值(一个指针最长为一个uint64_t)。
与现有技术相比,本发明的有益效果是:能够解决两线程之间数据传输,即对队列容器这个临界资源实现无锁化的访问,极大提高性能;以一种性能极高的方式实现了临界区的高效读写。
具体实施方式
为了使本发明的目的、技术方案及优点更加清楚明白,以下结合实施例,对本发明进行进一步详细说明。应当理解,此处所描述的具体实施例仅用以解释本发明,并不用于限定本发明。
本说明书(包括摘要)中公开的任一特征,除非特别叙述,均可被其他等效或者具有类似目的的替代特征加以替换。即,除非特别叙述,每个特征只是一系列等效或类似特征中的一个例子而已。
具体实施例1
一种多线程数据传输方法,具体方法为:
初始化系统时,申请一个能够容纳N个指针的连续内存空间;所述N=2m,m为大于等于1的整数;每个指针位置为一个单位,N个指针位置组成一个数组空间dataPtrArray;所述指针指向用户数据地址;
定义两个整数值:writePos和readPos;其中,writePos表示数据写入线程当前正要写入所述数组空间的位置;readPos表示数据读取线程当前正要从所述数组空间读取数据的位置;数据写入成功,则writePos值自增;数据读取成功,则readPos值自增,将被成功读取的数组空间位置的指针值清零,清零后,写入线程再发现这个位置是零,才能够正常写入新的用户指针数据;
当dataPtrArray写满数据但是并未被及时读取走时,会写入失败;当dataPtrArray中所有值都为零时,会读取失败;
定义一个整数掩码PosMask,其值为N-1;通过PosMask的值与读写位置的值做与运算,来决定当前正要写入和读取的真正的数组空间的位置,即当前写入的位置应该是dataPtrArray[writePos&PosMask],读取的位置是dataPtrArray[readPos&PosMask]。
具体实施例2
在具体实施例1的基础上,所述方法还包括:写入数据时,采用Intel的锁总线技术,以原子的方式,同时判断写入位置是否为空,若为空,则写入,若不为空,则表示队列已经写满了,此时写入线程陷入等待:
void* dataPtr是一个需要被写入队列的数据指针,
do{
uint32_t writeLocation = _sync_fetch_and_add(&writePos,1);
uint64_t* writePtr = (uint64_t*)dataPtrArray[PosMask&writeLocation];
if(__sync_bool_compare_and_swap(writePtr,0,(uint64_t)dataPtr))
{
notifyRead();
return true; //写入成功
}
else
{
wait();
}
}
读取数据时,采用Intel的锁总线技术,以原子方式的判断读取位置是否为空,若不为空,则读取返回,并且将读取位置清零,若为空,则表示整个队列都已经读取完,此时读取线程陷入等待:
uint64_t readPtrValue = 0,是从队列中读取的值,被返回给使用者,
uint32_t readLocaltion = __sync_fetch_and_add(&readPos,1);
uint64_t* readPtr = (uint64_t*)dataPtrArray[PosMask&readLocaltion];
if((readPtrValue =__sync_lock_test_and_set(readPtr,0)) != 0 )
{
notifyWrite();
return readPtrValue; //读取成功
}
else
{
wait();
}
当成功的写入一个数据指针时通知读取线程数据可读取,此时,如果读取线程为休眠状态,则将被唤醒;当成功的读取一个数据指针时通知写入线程数据可写入,此时,如果写入线程为休眠状态,则将被唤醒。
具体实施例3
在具体实施例1或2的基础上,所述内存空间中,以每8个字节为一个单位。内存空间以8个字节,即一个uint64_t为一个单位,可以存放任意系统下的sizeof(void*)的值,即存储一个指针的值(一个指针最长为一个uint64_t)。