简单计数器是否需要实现信号量或互斥量?
Is implementing semaphore or mutex necessary for simple counter?
我尝试实现计算某种积分的程序。为了加快计算速度,一个创建多个进程,另一个使用多个线程。在我的程序中,每个进程在共享内存中添加一个double值,每个线程通过指针添加一个double值。
这是我的问题。 add 操作显然是从内存中加载值,向其添加一个值,并将结果存储到内存中。因此,我的代码似乎很容易出现生产者-消费者问题,因为许多 processes/threads 访问同一内存区域。但是,我找不到有人使用信号量或互斥量来实现简单累加器的情况。
// creating processes
while (whatever)
{
pid = fork();
if (pid == 0)
{
res = integralproc(clist, m, tmpcnt, tmpleft, tmpright);
*(createshm(shm_key)) += res;
exit(1);
}
}
// creating or retrieving shared memory
long double* createshm(int key)
{
int shm_id = -1;
void* shm_ptr = (void*)-1;
while (shm_id == -1)
{
shm_id = shmget((key_t)key, sizeof(long double), IPC_CREAT | 0777);
}
while (shm_ptr == (void*)-1)
{
shm_ptr = shmat(shm_id, (void*)0, 0);
}
return (long double*)shm_ptr;
}
// creating threads
while (whatever)
{
threadres = pthread_create(&(targs[i]->thread_handle), NULL, integral_thread, (void*)targs[i]);
}
// thread function. targ->resptr is pointer that we add the result to.
void *integral_thread(void *arg)
{
threadarg *targ = (threadarg*)arg;
long double res = integralproc(targ->clist, targ->m, targ->n, targ->left, targ->right);
*(targ->resptr) += res;
//printf("thread %ld calculated %Lf\n", targ->i, res);
pthread_exit(NULL);
}
所以我就这样实现了,到现在为止不管做了多少processes/threads,结果就好像没发生过一样。
我担心我的代码可能仍然存在潜在危险,只是刚好在我的视线之外。
这段代码真的没有这些问题吗?还是我忽略了什么,是否应该修改代码?
如果您的线程都在竞相更新同一个对象(即,每个线程的 targ->resptr
指向同一事物),那么是的 - 您确实存在数据竞争,并且您会看到不正确的结果(很可能,"lost updates" 碰巧同时完成的两个线程尝试更新总和,但只有其中一个有效)。
你可能没有看到这个,因为你的 integralproc()
函数的执行时间很长,所以多个线程同时到达更新点的机会 *targ->resptr
很低。
尽管如此,您仍然应该解决问题。您可以在总和更新周围添加互斥锁 lock/unlock:
pthread_mutex_lock(&result_lock);
*(targ->resptr) += res;
pthread_mutex_unlock(&result_lock);
(这应该不会影响解决方案的效率,因为您在每个线程的生命周期中只锁定和解锁一次)。
或者,您可以让每个线程在其自己的线程参数结构中记录自己的部分结果:
targ->result = res;
然后,一旦所有工作线程都被 pthread_join()
ed,创建它们的父线程就可以遍历所有线程参数结构并将部分结果相加。
这里不需要额外的锁定,因为工作线程不访问彼此的结果变量,并且 pthread_join()
在设置结果的工作线程和读取它的父线程之间提供了必要的同步。
我尝试实现计算某种积分的程序。为了加快计算速度,一个创建多个进程,另一个使用多个线程。在我的程序中,每个进程在共享内存中添加一个double值,每个线程通过指针添加一个double值。
这是我的问题。 add 操作显然是从内存中加载值,向其添加一个值,并将结果存储到内存中。因此,我的代码似乎很容易出现生产者-消费者问题,因为许多 processes/threads 访问同一内存区域。但是,我找不到有人使用信号量或互斥量来实现简单累加器的情况。
// creating processes
while (whatever)
{
pid = fork();
if (pid == 0)
{
res = integralproc(clist, m, tmpcnt, tmpleft, tmpright);
*(createshm(shm_key)) += res;
exit(1);
}
}
// creating or retrieving shared memory
long double* createshm(int key)
{
int shm_id = -1;
void* shm_ptr = (void*)-1;
while (shm_id == -1)
{
shm_id = shmget((key_t)key, sizeof(long double), IPC_CREAT | 0777);
}
while (shm_ptr == (void*)-1)
{
shm_ptr = shmat(shm_id, (void*)0, 0);
}
return (long double*)shm_ptr;
}
// creating threads
while (whatever)
{
threadres = pthread_create(&(targs[i]->thread_handle), NULL, integral_thread, (void*)targs[i]);
}
// thread function. targ->resptr is pointer that we add the result to.
void *integral_thread(void *arg)
{
threadarg *targ = (threadarg*)arg;
long double res = integralproc(targ->clist, targ->m, targ->n, targ->left, targ->right);
*(targ->resptr) += res;
//printf("thread %ld calculated %Lf\n", targ->i, res);
pthread_exit(NULL);
}
所以我就这样实现了,到现在为止不管做了多少processes/threads,结果就好像没发生过一样。 我担心我的代码可能仍然存在潜在危险,只是刚好在我的视线之外。 这段代码真的没有这些问题吗?还是我忽略了什么,是否应该修改代码?
如果您的线程都在竞相更新同一个对象(即,每个线程的 targ->resptr
指向同一事物),那么是的 - 您确实存在数据竞争,并且您会看到不正确的结果(很可能,"lost updates" 碰巧同时完成的两个线程尝试更新总和,但只有其中一个有效)。
你可能没有看到这个,因为你的 integralproc()
函数的执行时间很长,所以多个线程同时到达更新点的机会 *targ->resptr
很低。
尽管如此,您仍然应该解决问题。您可以在总和更新周围添加互斥锁 lock/unlock:
pthread_mutex_lock(&result_lock);
*(targ->resptr) += res;
pthread_mutex_unlock(&result_lock);
(这应该不会影响解决方案的效率,因为您在每个线程的生命周期中只锁定和解锁一次)。
或者,您可以让每个线程在其自己的线程参数结构中记录自己的部分结果:
targ->result = res;
然后,一旦所有工作线程都被 pthread_join()
ed,创建它们的父线程就可以遍历所有线程参数结构并将部分结果相加。
这里不需要额外的锁定,因为工作线程不访问彼此的结果变量,并且 pthread_join()
在设置结果的工作线程和读取它的父线程之间提供了必要的同步。