【问题标题】:Is implementing semaphore or mutex necessary for simple counter?简单计数器是否需要实现信号量或互斥量?
【发布时间】:2019-04-10 17:57:12
【问题描述】:

我尝试实现计算某种积分的程序。为了加快计算速度,一个创建多个进程,另一个使用多个线程。在我的程序中,每个进程将一个双精度值添加到共享内存中,每个线程通过指针添加一个双精度值。

这是我的问题。加法操作显然从内存中加载值,向其中添加一个值,然后将结果存储到内存中。所以看起来我的代码很容易出现生产者-消费者问题,因为许多进程/线程访问相同的内存区域。但是,我找不到有人使用信号量或互斥锁来实现简单累加器的情况。

// 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);
}

所以我以这种方式实现它,到目前为止,无论我创建多少进程/线程,结果都好像从未发生过一样。 我担心我的代码可能仍然存在潜在危险,只是几乎不在我的视线范围内。 这段代码真的不受这些问题的影响吗?还是我忽略了什么,应该修改代码?

【问题讨论】:

  • 除非您使用某种锁,否则从技术上讲它是未定义的,或者最好使计数器成为原子并对其使用原子操作。
  • 我建议为整数编写一个简单的程序。让多个线程在不同步的情况下并行递增 (volatile) 整数固定次数。将结果与固定的增量数乘以线程数进行比较。阅读有关原子性和未定义行为的信息。
  • 为什么不为每个线程设置一个单独的计数器并在最后合并它们?
  • 另外,要非常小心,不要将 volatile 用作穷人的原子,这只是对 volatile 的错误使用。 C 有一个定义明确的线程内存模型标准化,基本上它采用了标准 C++ 并发/原子性模型。

标签: c pthreads fork shared-memory


【解决方案1】:

如果您的线程都在竞相更新同一个对象(即,每个线程的 targ->resptr 指向同一事物),那么是的 - 您确实存在数据竞争并且您会看到不正确的结果(可能,“丢失更新”,其中两个碰巧同时完成的线程尝试更新总和,并且只有一个有效)。

您可能没有看到这一点,因为您的integralproc() 函数的执行时间很长,因此多个线程同时到达更新*targ->resptr 的机会很低。

尽管如此,您仍应解决此问题。您可以在总和更新周围添加互斥锁/解锁:

pthread_mutex_lock(&result_lock);
*(targ->resptr) += res;
pthread_mutex_unlock(&result_lock);

(这不应该影响解决方案的效率,因为您在每个线程的生命周期中只锁定和解锁一次)。

或者,您可以让每个线程在自己的线程参数结构中记录自己的部分结果:

targ->result = res;

然后,一旦所有工作线程都被pthread_join()ed,创建它们的父线程就可以遍历所有线程参数结构并将部分结果相加。

这里不需要额外的锁定,因为工作线程不访问彼此的结果变量,pthread_join() 在设置结果的工作线程和读取结果的父线程之间提供了必要的同步。

【讨论】:

  • 我想那时实现互斥锁或信号量是必要且安全的。感谢您的友好回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-24
  • 2012-08-13
  • 2011-09-06
  • 1970-01-01
  • 2012-09-01
  • 2013-12-07
  • 2011-04-20
相关资源
最近更新 更多