简体   繁体   English

在C中使用互斥锁同步pthread

[英]Synchronizing pthreads using mutex in C

I've got to write a program that counts series of first 10 terms (sorry for my language, this is the first time that I'm talking about math in english) given by formula (x^i)/i!. 我必须编写一个程序,该程序计算由公式(x ^ i)/ i!给出的前10个术语(对不起,对于我的语言,这是我第一次谈论英语数学)。 So, basically it's trivial. 因此,基本上这是微不足道的。 BUT, there's some special requirements. 但是,有一些特殊要求。 Every single term got to be counted by seperated thread, each of them working concurrent. 每个单独的术语都必须由单独的线程计算,每个线程并发工作。 Then all of them got to save results to common variable named result . 然后他们都将结果保存到名为result的公共变量中。 After that they have to be added by main thread, which will display final result. 之后,必须通过主线程添加它们,这将显示最终结果。 All of it using pthreads and mutexes. 所有这些都使用pthread和互斥锁。

That's where I have a problem. 那就是我有问题的地方。 I was thinking about using table to store results, but I was told by teacher, that it's not correct solution, cause then I don't have to use mutexes. 我当时在考虑使用表来存储结果,但是老师告诉我,这不是正确的解决方案,因为这样我就不必使用互斥体了。 Any ideas what to do and how to synchronize it? 有什么想法怎么做以及如何使其同步? I'm completely new to pthread and mutex. 我对pthread和互斥锁是完全陌生的。

Here's what I got till now. 这就是我到目前为止所得到的。 I'm still working on it, so it's not working at the moment, it's just a scheme of a program, where I want to add mutexes. 我仍在处理它,因此目前无法使用,这只是一个程序的方案,我想在其中添加互斥体。 I hope it's not all wrong. 我希望这不是全部错误。 ;p ,p

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

int number = 0;
float result = 0;
pthread_mutex_t term_lock;
pthread_mutex_t main_lock;
int save = 0; //condition variable

int factorial(int x) { 
        if(x==0 || x==1)
                return 1;

        return factorial(x-1)*x;
}

void  *term(void *value) {  
        int x = *(int *)value;
        float w;
        if(save == 0) {
            pthread_mutex_lock(&term_lock);
            w = pow(x, number)/factorial(number);
            result = w;
            printf("%d term of series with x: %d  is: %f\n", number, x, w);
            number++;
            save = 1;
            pthread_mutex_unlock(&term_lock);
        }
        return NULL;
}

int main(void) {

        int x, i, err = 0;
        float final = 0;
        pthread_t threads[10];

        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

        printf("Get X: \n");
        scanf("%d", &x);
        for(i=0; i<10; i++)
        {
                err = pthread_create(&threads[i], &attr, (void *)term, &x);
                if(err) {
                        printf("Error creating threads.\n");
                        exit(-1);
                }
        }
        i = 0;
        while (number <= 10) {
            //printf("While Result: %f, final %f\n", result, final); - shows that it's infinite loop
            if(save) {
                pthread_mutex_lock(&main_lock); 
                final = final + result;
                save = 0;
                pthread_mutex_unlock(&main_lock);   
                printf("If Result: %f, final %f\n", result, final); //final == last result
            }
        }
        return 0;
}

EDIT: If it's not clear - I need help with solution how to store results of all threads in common variable and synchronizing it. 编辑:如果不清楚-我需要解决方案的帮助,该解决方案如何将所有线程的结果存储在公共变量中并进行同步。

EDIT2: Possible solution - global variable result shared by all threads. EDIT2:可能的解决方案-所有线程共享全局变量result Returned to main thread, it would be added to some local variable, so then I could just overwrite it's value with result from another thread. 返回主线程后,它将被添加到一些局部变量中,因此我可以用另一个线程的结果覆盖它的值。 Of course it will require some synchronization, so another thread won't overwrite it before I add it in main thread. 当然,这将需要一些同步,因此在我将其添加到主线程之前,另一个线程不会覆盖它。 What do you think? 你怎么看?

EDIT3: I've updated code with what I have right now. EDIT3:我已经用现在的内容更新了代码。 Output is giving me me values of 8-9 terms (printf in term), then program is still working, showing nothing. 输出给我的值是8-9项(以printf表示),然后程序仍在工作,什么也没显示。 Commented printf showed me, that while loop is infinite. 评论过的printf向我展示了while循环是无限的。 Also local variable final has just last value of result. 同样,局部变量fi​​nal具有结果的最后一个值。 What am I doing wrong? 我究竟做错了什么?

It's rather contrived that the main thread should be the one to add the terms, but the individual threads must all write their results to the same variable. 相当虚构的是,主线程应该是添加术语的线程,但是各个线程必须全部将其结果写入同一变量。 I would ordinarily expect each thread to add its own term to the result (which does require mutex), or possibly to put its result in an array (as you suggested), or to add it to a shared queue (which would require mutex), or even to write it to a pipe. 我通常希望每个线程在结果中添加自己的条件(这确实需要互斥体),或者可能将其结果放入数组中(如您建议的那样),或者将其添加到共享队列中(这需要互斥体) ,甚至将其写入管道。 Nevertheless, it can be done your teacher's way. 不过,可以按照您老师的方式完成。

One of the key problems to solve is that you have to distinctly different operations that you need to synchronize: 要解决的关键问题之一是您必须进行完全不同的同步操作:

  • The various computational threads' writes to the shared result variable 各种计算线程将写入共享结果变量
  • The main thread's reads of the result variable 主线程读取结果变量

You cannot use just a single synchronization construct because you cannot that way distinguish between the computational threads and the main thread. 您不能仅使用单个同步构造,因为您不能通过这种方式在计算线程和主线程之间进行区分。 One way to approach this would be to synchronize the computational threads' writes via a mutex, as required, and to synchronize those vs . 解决此问题的一种方法是根据需要通过互斥锁同步计算线程的写入,并与vs同步。 the main thread's reads via semaphores or condition variables. 主线程通过信号量或条件变量进行读取。 You could also do it with one or more additional mutexes, but not cleanly. 您也可以使用一个或多个其他互斥锁来做到这一点,但并非一帆风顺。

Additional notes: 补充笔记:

  • the result variable in which your threads deposit their terms must be a global. 您的线程在其中存放其条件的结果变量必须是全局变量。 Threads do not have access to the local variables of the function from which they are launched. 线程无权访问从中启动它们的函数的局部变量。
  • the signature of your term() function is incorrect for a thread start function. 您的term()函数的签名对于线程启动函数不正确。 The argument must be of type void * . 参数必须为void *类型。
  • thread start functions are no different from other functions in that their local variables are accessible only for the duration of the function execution. 线程启动函数与其他函数没有什么不同,它们的局部变量仅在函数执行期间才可访问。 In particular, returning a pointer to a local variable cannot do anything useful, as any attempt to later dereference such a pointer produces undefined behavior. 特别是,将指针返回到局部变量不能做任何有用的事情,因为任何以后取消引用此类指针的尝试都会产生未定义的行为。

I'm not going to write your homework for you, but here's an approach that can work: 我不会为您写作业,但这是一种可行的方法:

  1. The main thread initializes a mutex and two semaphores, the latter with initial values zero. 主线程初始化一个互斥锁和两个信号量,后者的初始值为零。
  2. The main thread launches all the computational threads. 主线程启动所有计算线程。 Although it's ugly, you can feed them their numeric arguments by casting those to void * , and then casting them back in the term() function (since its argument should be a void * ). 虽然很丑陋,但您可以通过将其强制转换为void * ,然后将其强制返回term()函数(因为其参数应为void * )来为其提供数字参数。
  3. The main thread then loops. 然后主线程循环。 At each iteration, it 在每次迭代中
    1. waits for semaphore 1 ( sem_wait() ) 等待信号量1( sem_wait()
    2. adds the value of the global result variable to a running total 将全局result变量的值添加到运行总计
    3. posts to semaphore 2 ( sem_post() ) 张贴到信号量2( sem_post()
    4. if as many iterations have been performed as there are threads, breaks from the loop 如果执行的迭代次数与线程数相同,则中断循环

Meanwhile, each computational thread does this: 同时,每个计算线程都这样做:

  1. Computes the value of the appropriate term 计算适当项的值
  2. locks the mutex 锁定互斥锁
  3. stores the term value in the global result variable 将术语值存储在全局result变量中
  4. posts to semaphore 1 张贴信号量1
  5. waits for semaphore 2 等待信号量2
  6. unlocks the mutex 解锁互斥锁

Update: 更新:

To use condition variables for this job, it is essential to identify which shared state is being protected by those condition variables, as one must always protect against waking spurriously from a wait on a condition variable. 要使用条件变量来完成这项工作,必须确定那些条件变量正在保护哪个共享状态,因为必须始终防止他人因等待条件变量而突然醒来。

In this case, it seems natural that the shared state in question would involve the global result variable in which the computational threads return their results. 在这种情况下,看起来很自然,所讨论的共享状态将包含全局result变量,计算线程将在其中返回其结果。 There are really two general, mutually exclusive states of that variable: 该变量实际上有两个通用的互斥状态:

  1. Ready to receive a value from a computational thread, and 准备从计算线程接收值,并且
  2. Ready for the main thread to read. 准备主线程读取。

The computational threads need to wait for the first state, and the main thread needs to wait (repeatedly) for the second. 计算线程需要等待第一个状态,而主线程需要(重复)等待第二个状态。 Since there are two different conditions that threads will need to wait on, you need two condition variables. 由于线程需要等待两个不同的条件,因此需要两个条件变量。 Here's an alternative approach using these ideas: 这是使用这些想法的另一种方法:

  1. The main thread initializes a mutex and two condition variables, and sets result to -1 . 主线程初始化一个互斥锁和两个条件变量,并将result设置为-1
  2. The main thread launches all the computational threads. 主线程启动所有计算线程。 Although it's ugly, you can feed them their numeric arguments by casting those to void * , and then casting them back in the term() function (since its argument should be a void * ). 虽然很丑陋,但您可以通过将其强制转换为void * ,然后将其强制返回term()函数(因为其参数应为void * )来为其提供数字参数。
  3. The main thread locks the mutex 主线程锁定互斥锁
  4. The main thread then loops. 然后主线程循环。 At each iteration, it 在每次迭代中
    1. tests whether result is non-negative. 测试result是否为负数。 If so, it 如果是这样
      1. adds the value of result variable to a running total result变量的值添加到运行总计
      2. if as many terms have been added as there are threads, breaks from the loop 如果添加的术语与线程的数量一样多,则中断循环
      3. sets result to -1 . result设置为-1
      4. signals condition variable 1 信号条件变量1
    2. waits on condition variable 2 等待条件变量2
  5. Having broken from the loop, the main thread unlocks the mutex 从循环中断后,主线程将互斥锁解锁

Meanwhile, each computational thread does this: 同时,每个计算线程都这样做:

  1. Computes its term 计算其期限
  2. Locks the mutex 锁定互斥锁
  3. Loops: 循环:
    1. Checks the value of result . 检查result的值。 If it is less than zero then breaks from the loop 如果小于零,则从循环中断
    2. waits on condition variable 1 等待条件变量1
  4. Having broken from the loop, sets result to the computed term 脱离循环后,将result设置为计算项
  5. signals condition variable 2 信号条件变量2
  6. unlocks the mutex 解锁互斥锁

The number is shared between all the threads, so you will need to protect that with a mutex (which is probably what your teacher is wanting to see) number在所有线程之间共享,因此您需要使用互斥锁来保护它(这可能是您的老师希望看到的)

pthread_mutex_t number_mutex;
pthread_mutex_t result_mutex;
int number = 0;
int result = 0;

void  *term(int x) {
  float w;

  // Critical zone, make sure only one thread updates `number`
  pthread_mutex_lock(&number_mutex); 
  int mynumber = number++;
  pthread_mutex_unlock(&number_mutex);
  // end of critical zone 

  w = pow(x, mynumber)/factorial(mynumber);
  printf("%d term of series with x: %d  is: %f\n", mynumber, x, w);

  // Critical zone, make sure only one thread updates `result`
  pthread_mutex_lock(&result_mutex); 
  result += w;
  pthread_mutex_unlock(&result_mutex);
  // end of critical zone 

  return (void *)0;
}

You should also remove the DETACHED state and do a thread-join at the end of your main program before printing out the result 您还应该删除DETACHED状态,并在输出结果之前在主程序的末尾进行线程联接。

Here is my solution to your problem: 这是我为您解决的问题:

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

int number=0;
float result[10];
pthread_mutex_t lock;

int factorial(int x) {


    if(x==0 || x==1)
                return 1;

        return factorial(x-1)*x;
}

void  *term(void *value) {
        int x = *(int *)value;
        float w;
        pthread_mutex_lock(&lock);
        w = pow(x, number)/factorial(number);
        printf("%d term of series with x: %d  is: %f\n", number, x, w);
        result[number] = w;
        number++;
        pthread_mutex_unlock(&lock);
        return NULL;
}

int main(void) {

        int x, i, err;
        pthread_t threads[10];

        printf("Get X: \n"); 
        scanf("%d", &x);

        for(i=0; i<=9; i++)
        {
                err = pthread_create(&threads[i], NULL, term, &x);
                if(err) {
                        printf("Error creating threads.\n");
                        exit(-1);
                }
        }

        for(i = 0; i < 10; i++)
        {
                pthread_join(threads[i], NULL);
        }

        i = 0;

        for(i=0; i<=9; i++)
        {
                printf("%f\n", result[i]);
        }
        return 0;
}

This code creates a global mutex pthread_mutex_t lock that (in this case) makes sure that same code is not executed by anyone at the same time: basically when one thread executes pthread_mutex_lock(&lock) , it forbids any other thread from executing that part of the code until the "original" thread executes pthread_mutex_unlock(&lock) . 此代码创建一个全局互斥锁pthread_mutex_t lock (在这种情况下)确保任何人都不会同时执行相同的代码:基本上,当一个线程执行pthread_mutex_lock(&lock) ,它将禁止任何其他thread执行该部分代码。直到“原始”线程执行pthread_mutex_unlock(&lock)为止。

The other important part is pthread_join : what this does is force the main thread to wait for the execution of every other thread created; 另一个重要的部分是pthread_join :强制主线程等待创建的所有其他线程的执行。 this way, float result[10] is written before actually being worked on in the main thread (in this case, the last print instruction). 这样,在实际在主线程中进行处理之前(在此情况下,最后一条打印指令),将写入float result[10]

Other than that, I fixed a couple of bugs in your code that other users pointed out. 除此之外,我修复了其他用户指出的代码中的一些错误。

If result is to be a single variable, then one solution is to use an array of 20 mutexes: aMutex[20];. 如果结果是单个变量,则一种解决方案是使用20个互斥量的数组:aMutex [20];。 Main locks all 20 mutexes then starts the pthreads. Main锁定所有20个互斥锁,然后启动pthread。 Each pthread[i] computes a local term, waits for aMutex[i], stores it's value into result, then unlocks aMutex[10+i]. 每个pthread [i]计算一个本地项,等待aMutex [i],将其值存储到结果中,然后解锁aMutex [10 + i]。 In main() for(i = 0; i < 20; i++){ unlock aMutex[i] to allow pthread[i] to store its value into result, then wait for aMutex[10+i] to know that result is updated, then add result to a sum. 在main()中,for(i = 0; i <20; i ++){解锁aMutex [i]以允许pthread [i]将其值存储到结果中,然后等待aMutex [10 + i]知道结果已更新,然后将结果相加。 } }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM