我正在尝试编写一个创建两个线程的程序:“前端”和“后端”线程。我想创建一个“后端”线程来迭代和计算斐波那契序列中的术语对,并将它们放入一个数组中,并创建一个“前端”线程,该线程将在每次迭代时打印出该数组对。
“ Front-End”线程-用于在每次迭代中显示“ Back-End”线程操作的结果
“后端”线程-用于计算和设置数组
即[5,8],并在迭代后将包含[13,21]
我正在努力在线程中实现斐波那契序列部分,并且取得了以下进展:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
int fib;
void *front_end(void *ptr);
void *back_end(void *ptr);
int main() {
pthread_t thread1, thread2;
int arr[2] = {5,8};
const int *ptrtoarr;
ptrtoarr=arr;
int create1, create2;
int *s=(int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr++;
s = (int *)(ptrtoarr);
printf("%d \n", *s);
ptrtoarr--;
create1 = pthread_create(&thread1, NULL, back_end, &arr);
if(create1) {
fprintf(stderr,"Error - pthread_create() return code: %d\n",create1);
exit(EXIT_FAILURE);
}
pthread_join(thread1, NULL);
//pthread_join(thread2, NULL);
}
// front-end thread to be callback for each back-end iteration
void *front_end(void *ptr) {
int *sum = ptr;
int i, upper = atoi(ptr);
if (upper > 0) {
for (i=0; i<upper; i++){
//Print the fib pairs
}
}
pthread_exit(0);
}
void *back_end(void *ptr) {
int i, upper = atoi(ptr);
fib=1;
if(upper > 0) {
int pre1 = 0;
int current;
//calc fib numbers.....
if(fib == 1){
printf("")
}
}
}
有人可以指导我逐步解决这个问题吗?
您的骨骼需要工作。
假设以下内容:
unsigned n = ...;
unsigned n_ready = 0; // How many are ready to print
unsigned *fibs = malloc(sizeof(unsigned)*n);
fibs[0] = 0;
fibs[1] = 1;
在后端工作人员的核心,您将有
for (unsigned i=2; i<n; ++i) {
fibs[i] = fibs[i-2] + fibs[i-1];
n_ready = i+1;
}
在您的前端工作人员的核心,您将有
for (unsigned i=0; i<n; ++i) {
while (i >= n_ready)
/* Nothing */;
printf("%u\n", fibs[i]);
}
问题1
如果一个线程在另一个变量写入变量时尝试读取该变量,则会遇到问题。两个或多个线程同时读取同一变量是可以的。
两个线程使用的变量是n
,fib[]
和n_ready
的元素。
n
:任一线程均未更改,因此我们无需控制对其的访问。
[fib[i]
for i >= n_ready
:仅由后端工作人员访问,因此我们无需控制对这些对象的访问。
[fib[i]
for i < n_ready
:仅由前端工作人员访问,因此我们无需控制对这些访问。”]]
n_ready
:后端工作人员可以随时设置n_ready
,而前端工作人员可以随时尝试读取n_ready
,因此我们确实需要控制对n_ready
的访问。
Mutex通常用于确保一次只有一个线程正在访问资源(例如,变量,变量组,文件句柄等)。
我们的后端工作人员成为
for (unsigned i=2; i<n; ++i) { // The mutex only protects n_ready // --nothing is going to touch fib[i]-- // so we don't need to obtain a lock yet. pthread_mutex_unlock(&mutex); fibs[i] = fibs[i-2] + fibs[i-1]; // We need to access n_ready. pthread_mutex_lock(&mutex); n_ready = i+1; pthread_mutex_unlock(&mutex); }
我们的前端工作者成为
for (unsigned i=0; i<n; ++i) { // We need to access n_ready. pthread_mutex_lock(&mutex); while (i >= n_ready) { // Allow other thread to gain the lock. pthread_mutex_unlock(&mutex); // We need to access n_ready. pthread_mutex_lock(&mutex); } // The mutex only protects n_ready // --nothing is going to change fib[i]-- // so we can release it now rather than later. pthread_mutex_unlock(&mutex); printf("%u\n", fibs[i]); }
问题2
您有一个忙碌的循环。通常,这很糟糕,因为这意味着您的线程正在使用100%的等待时间。 (在这种特殊情况下,由于i >= n_ready
可能已经是对的,这实际上是一个不错的策略。但是让我们忽略它。)一个线程可以休眠,直到另一个线程使用条件vars发出信号为止。]
我们的后端工作人员成为
for (unsigned i=2; i<n; ++i) { // The mutex only protects n_ready // --nothing is going to touch fib[i]-- // so we don't need to obtain a lock yet. fibs[i] = fibs[i-2] + fibs[i-1]; // We need to access n_ready. pthread_mutex_lock(&mutex); n_ready = i+1; // Wake up the other thread if it's blocked. pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); }
我们的前端工作者成为
for (unsigned i=0; i<n; ++i) { // We need to access n_ready. pthread_mutex_lock(&mutex); while (i >= n_ready) pthread_cond_wait(&cond, &mutex); // The mutex only protects n_ready // --nothing is going to change fib[i]-- // so we can release it now rather than later. pthread_mutex_unlock(&mutex); printf("%u\n", fibs[i]); }
始终在锁定的互斥锁上调用
pthread_cond_wait
。互斥量在调用时将解锁,并在返回之前将其锁定。这允许另一个线程获取互斥体以更改n_ready
。完整代码:
#include <errno.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define UNUSED(x) (void)(x) // To control access to n_ready. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static unsigned n_ready = 0; // How many are ready to print static unsigned n; static unsigned *fibs = NULL; static void *back_worker(void *unused) { UNUSED(unused); fibs[0] = 0; fibs[1] = 1; // We need to access n_ready. pthread_mutex_lock(&mutex); n_ready = 2; // Wake up the other thread if it's blocked. pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); for (unsigned i=2; i<n; ++i) { // The mutex only protects n_ready // --nothing is going to touch fib[i]-- // so we don't need to obtain a lock yet. fibs[i] = fibs[i-2] + fibs[i-1]; // We need to access n_ready. pthread_mutex_lock(&mutex); n_ready = i+1; // Wake up the other thread if it's blocked. pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } return NULL; } static void *front_worker(void *unused) { UNUSED(unused); for (unsigned i=0; i<n; ++i) { // We need to access n_ready. pthread_mutex_lock(&mutex); while (i >= n_ready) pthread_cond_wait(&cond, &mutex); // The mutex only protects n_ready // --nothing is going to change fib[i]-- // so we can release it now rather than later. pthread_mutex_unlock(&mutex); printf("%u\n", fibs[i]); } return NULL; } int main(void) { n = 20; // How many to generate. fibs = malloc(sizeof(unsigned) * n); pthread_t back_thread; if (errno = pthread_create(&back_thread, NULL, back_worker, NULL)) { perror(NULL); exit(1); } pthread_t front_thread; if (errno = pthread_create(&front_thread, NULL, front_worker, NULL)) { perror(NULL); exit(1); } pthread_join(back_thread, NULL); pthread_join(front_thread, NULL); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); free(fibs); return 0; }
输出:
$ gcc -Wall -Wextra -pedantic a.c -o a -lpthread && a 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181