使用参数创建线程的最有效方法是什么?参数是一个结构,如果结构不能保留在父线程堆栈上,则有两种解决方案。
动态内存分配
struct Arg{
int x;
int y;
};
void* my_thread(void* v_arg){
Arg* arg = (Arg*) v_arg;
//... running
delete arg;
return NULL;
}
//Creating a thread
void a_function(){
Arg* arg = new Arg;
arg->x = 1; arg->y = 2;
pthread_t t;
pthread_create(&t, NULL, my_thread, arg);
pthread_detach(t);
}
使用信号量
struct Arg{
sem_t sem;
int x;
int y;
};
void* my_thread(void* v_arg){
Arg* arg = (Arg*) v_arg;
int arg_x = v_arg->x;
int arg_y = v_arg->y;
sem_post( &(v_arg->sem) );
//... running
return NULL;
}
//Creating a thread
void a_function(){
Arg arg;
arg.x = 1; arg.y = 2;
sem_init( &(arg.sem), 0, 0);
pthread_t t;
pthread_create(&t, NULL, my_thread, &arg);
pthread_detach(t);
sem_wait( &(arg.sem) );
sem_destroy( &(arg.sem) );
}
我使用Linux和Windows。
答案 0 :(得分:2)
在您发布的代码中,最有效的实现是使用堆分配(您的第一个示例)。原因是堆分配(使用new()或malloc)比上下文切换便宜得多。考虑在第二个例子中需要发生什么:
或者,你的第一个例子:
答案 1 :(得分:0)
这取决于。如果你的结构不大,最好动态分配它以便最小化奇数同步调用。否则,如果您的结构非常大并且您为具有少量内存的系统编写代码,则最好使用信号量(甚至是condvar)。
答案 2 :(得分:0)
原子操作解决方案。这是为你的论点获取记忆的一种非常高速的方式。
如果参数的大小始终相同,则预先分配一堆参数。将pNext添加到结构中以将它们链接在一起。使用pNext创建一个_pRecycle全局,将所有可用的全局链接为链接列表。当你需要一个参数时,在垃圾邮件列表的头部使用原子操作来CAS。完成后,使用原子操作将arg放回垃圾列表的开头。
CAS指的是像__sync_bool_compare_and_swap这样的东西,它在成功时返回1.
获取参数记忆:
while (1) { // concurrency loop
pArg = _pRecycle; // _pRecycle is the global ptr to the head of the available arguments
// POINT A
if (CAS(&_pRecycle, pArg->pNext, pArg)) // change pRecycle to next item if pRecycle hasn't changed.
break; // success
// POINT B
}
// you can now use pArg to pass arguments
完成后回收参数内存:
while (1) { // concurrency loop
pArg->pNext = _pRecycle;
if (CAS(&_pRecycle, pArg, pArg->pNext)) // change _pRecycle to pArg if _pRecycle hasn't changed.
break; // success
}
// you have given the mem back
如果某些东西使用并且循环使用pArg,而另一个线程在A点和B点之间换出,则存在竞争条件。如果您的工作需要很长时间来处理,这不会成为问题。否则你需要对列表的头部进行版本化...要做到这一点,你需要能够一次原子地改变两件事...... Unions结合64位CAS来救援!
typedef union _RecycleList {
struct {
int iversion;
TArg *pHead;
}
unsigned long n64; // this value is iVersion and pHead at the same time!
} TRecycleList;
TRecycleList _Recycle;
获取记忆:
while (1) // concurrency loop
{
TRecycleList Old.n64 = _Recycle.n64;
TRecycleList New.n64 = Old.n64;
New.iVersion++;
pArg = New.pHead;
New.pHead = New.pHead->pNext;
if (CAS(&_Recycle.n64, New.n64, Old.n64)) // if version isnt changed we get mem
break; // success
}
把mem放回去:
while (1) // concurrency loop
{
TRecycleList Old.n64 = _Recycle.n64;
TRecycleList New.n64 = Old.n64;
New.iVersion++;
pArg->pNext = New.pHead;
New.pHead = pArg;
if (CAS(&_Recycle.n64, New.n64, Old.n64)) // if version isnt changed we release mem
break; // success
}
由于99.9999999%的时间没有两个线程同时执行代码来获取内存,所以你可以获得很好的性能。我们的测试表明,CAS只需设置_pRecycle = pRecycle-> pNext,速度只有2倍。 64位和128位CAS与32位一样快。基本上它尖叫。每隔一段时间,当两个线程实际竞争时,并发循环将执行两次。一个人总会赢,所以比赛解决得非常快。