C中的线程互相中断

问题描述 投票:-1回答:2

所以我有一个称为counter的全局变量,并且我运行4个线程,它们以百万次递增,但是最终我得到的结果甚至没有达到200万。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>


int nthread;
int counter=0;
void *f(void *arg)
{
    int i = *(int *)arg;
    int *p;
   for (int c = 0; c < 1000000; c++)
   {
       counter++;
   }

    printf(" I am thread %d (out of %d),tid =% ld\n", i, nthread, pthread_self());
    p = malloc(sizeof(int));
    *p = i * 2;
    pthread_exit(p); // return p
}

int main(int argc, char *argv[])
{
    pthread_t *tid;
    int e, i, *ti;
    nthread = 4;
    tid = malloc(nthread * sizeof(pthread_t));
    ti = malloc(nthread * sizeof(int));
    for (i = 0; i < nthread; i++)
    {
        ti[i] = i;
        if ((e = pthread_create(&tid[i], NULL, f, &ti[i])) != 0)
            send_error(e, " pthread_create ");
    }
    for (i = 0; i < nthread; i++)
    {
        void *r;
        if ((e = pthread_join(tid[i], &r)) != 0)
            send_error(e, " pthread_join ");
        printf(" Return of thread %d = %d\n", i, *(int *)r);
        free(r);
    }
    printf("counter is %d\n",counter);
    free(tid);
    free(ti);
}

是什么原因造成的,我该如何解决?PS:如果您的代码无法编译,请用printfs替换send_error

c pthreads
2个回答
0
投票

pthreads标准非常明确,您可能无法在一个线程正在或可能正在修改另一个线程时访问一个线程中的对象。您的代码违反了此规则。

此规则有很多原因,但最明显的是:

for (int c = 0; c < 1000000; c++)
{
    counter++;
}

您希望编译器像这样优化代码。您希望它可以将counter保留在寄存器中,甚至可以消除循环。但是,如果不要求您避免线程重叠修改和访问同一对象,则编译器将必须以某种方式证明在此代码运行时,任何其他线程中的其他代码都无法触及counter

这将导致无法对99%的不跨线程共享对象的代码进行大量有价值的优化,而仅仅是因为编译器无法证明访问可能重叠。

要求具有[[does具有重叠对象访问权限的代码以清楚地表明它们确实有此要求更有意义。每个线程标准都提供了执行此操作的好方法,包括pthreads。

您可以使用任何方法来防止您喜欢的此问题。使用互斥锁是最简单的方法,绝对是您应该首先学习的方法。

0
投票
Counter是一个全局变量,所有线程同时作用于它。语句counter ++是高级语言语句,在编译后会转换为汇编级语言的多个语句。

为简单起见,假设汇编中的counter ++可以实现为:

1. Fetch value of integer stored at address pointed by counter to a register 2. Increment value at register 3. Put the incremented value back at the memory location

所有3个汇编级别的语句不会一起出现。因此线程1可以首先获取该值。然后线程2获取该值。并增加它并存储它。然后,线程1执行汇编步骤2和3。但是,净增量为1而不是2,因为线程1没有更新的值。

[要克服这个问题,要么

((i)所有3种汇编语言都应同时出现

((ii)如果一个线程介于计数器++的执行之间,则其他线程不应开始执行。

第二个方法更易于实现,如@DavidSchwartz所指出的,可以使用互斥锁来完成。

#include <stdio.h> #include <pthread.h> #include <stdlib.h> pthread_mutex_t myMutex; int nthread; int counter=0; void *f(void *arg) { int i = *(int *)arg; int *p; for (int c = 0; c < 1000000; c++) { pthread_mutex_lock(&myMutex); counter++; pthread_mutex_unlock(&myMutex); } printf(" I am thread %d (out of %d),tid =% ld\n", i, nthread, pthread_self()); p = malloc(sizeof(int)); *p = i * 2; pthread_exit(p); // return p } int main(int argc, char *argv[]) { if (pthread_mutex_init(&myMutex, NULL) != 0) { printf("ERROR\n"); } pthread_t *tid; int e, i, *ti; nthread = 4; tid = malloc(nthread * sizeof(pthread_t)); ti = malloc(nthread * sizeof(int)); for (i = 0; i < nthread; i++) { ti[i] = i; if ((e = pthread_create(&tid[i], NULL, f, &ti[i])) != 0) printf("error"); } for (i = 0; i < nthread; i++) { void *r; if ((e = pthread_join(tid[i], &r)) != 0) printf("error"); printf(" Return of thread %d = %d\n", i, *(int *)r); free(r); } printf("counter is %d\n",counter); free(tid); free(ti); }

© www.soinside.com 2019 - 2024. All rights reserved.