本次上机主题为线程。一个程序可以包含多个进程,而一个进程又可以创建多个线程,每个线程都共享此进程的上下文环境。
在unix下使用gcc编译带有线程操作的程序时需要加上 -pthread选项。
基本的线程函数:
#include <pthread.h>
创建线程:后两个参数为待调用的函数及传递的参数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);在进程中等待相应线程的结束:
int pthread_join(pthread_t thread, void **retval);
1,创建一个能实现两数相加的线程,使用3个线程实现(a + b) * (c + d)/(e + f)。为此,我们使用一个结构体传递加数及结果:
typedef struct _Calcul { float op1, op2, result; } Calcul;
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
typedef struct _Calcul{ float op1, op2, result;}Calcul;
void somme(Calcul* calcul)
{
calcul->result = calcul->op1 + calcul->op2;
}
void main()
{
int val_return;
int **res1, res2, res3;
pthread_t ptr1, ptr2, ptr3;
Calcul calcul1, calcul2, calcul3;
calcul1.op1=1;
calcul1.op2=2;
calcul2.op1=3;
calcul2.op2=4;
calcul3.op1=5;
calcul3.op2=16;
if( (val_return=pthread_create(&ptr1, NULL, (void*)somme, (void*)(&calcul1) )) != 0)
{
perror("pthread_create");exit(1);
}
if( (val_return=pthread_create(&ptr2, NULL, (void*)somme, (void*)(&calcul2) )) != 0)
{
perror("pthread_create");exit(1);
}
if( (val_return=pthread_create(&ptr3, NULL, (void*)somme, (void*)(&calcul3) )) != 0)
{
perror("pthread_create");exit(1);
}
pthread_join( ptr1, NULL);//(void**)res1);
pthread_join( ptr2, NULL);//(void**)res1);
pthread_join( ptr3, NULL);//(void**)res1);
printf("(1+2)*(3+4)/(5+16)=%f\n", calcul1.result * calcul2.result / calcul3.result);
}
2,使用互斥(mutex)实现对共享资源的访问,涉及到的函数有:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
关于互斥,在这里就不详解了。总之如果有多个线程想要访问同一个资源(比如文件)的话,那么每个线程在访问共享资源之前都应该调用pthread_mutex_lock,成功返回后就好比给共享资源加了一把锁,其他的线程在调用这个函数时会被卡住,直到此进程将资源解锁:pthread_mutex_unlock。当然init和destroy分别用于在使用前初始化和用完摧毁这一互斥。
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void ecrire( pthread_mutex_t * mutex)
{
FILE* fd;
while(1)
{
pthread_mutex_lock(mutex);//c'est préférable de mettre les opération dans le corps de mutex
//fd = open("verrou.txt", O_WRONLY | O_CREAT);
fd = fopen("verrou.txt", "w");
if( fd==NULL ){perror("open");exit(errno);}
fputs("Je suis la fonction ecrire!", fd);
printf("Pthread ecrire : vient d'écrire une phrase...\n");
fclose(fd);
pthread_mutex_unlock(mutex);
sleep(10);
}
}
void lire( pthread_mutex_t * mutex)
{
FILE* fd;
char msg[50];
while(1)
{
pthread_mutex_lock(mutex);
fd = fopen("verrou.txt", "r");
if( fd==NULL ){perror("open");exit(errno);}
fgets( msg, 49, fd);
fclose(fd);
pthread_mutex_unlock(mutex);
printf("Pthread lire : %s\n", msg);
sleep(5);
}
}
pthread_mutex_t mutex;
void main()
{
int val_return;
pthread_t ptr1, ptr2;
pthread_mutex_init(&mutex, NULL);
//mutex = PTHREAD_MUTEX_INITIALIZER;
if( (val_return=pthread_create(&ptr1, NULL, (void*)ecrire, (void*)(&mutex) )) != 0)
{
perror("pthread_create");exit(1);
}
if( (val_return=pthread_create(&ptr2, NULL, (void*)lire, (void*)(&mutex) )) != 0)
{
perror("pthread_create");exit(1);
}
pthread_join( ptr1, NULL);//Si le proc termine, les threads sont aussi terminés
pthread_join( ptr2, NULL);//(void**)res1);
}
3,线程间的同步:barrier。
有这样一系列barrier函数可以在线程内部实现线程间的同步:
pthread_barrier_t barrier;
pthread_barrier_init(&barrier, NULL, THREAD_NUM);// Init the barrier with a number.
pthread_barrier_wait(&barrier);//The calling thread can continue only if there are THREAD_NUM threads who call this method.
pthread_barrier_destroy(&barrier);
简单地说,假如我们设置一个代表3个线程的barrier,并在3个线程中分别调用pthread_barrier_wait,那么只有当3个进程全部都执行到调用此函数的那行时,这3个线程才能继续向下进行。当第3个线程还没有到达此处时,前两个调用wait的线程都会被卡在那里,等待。
/*
The barrier is a way to synchronise threads during their executions,
while pthread_join(), which is called in the body of the process,
only synchronize the end of threads.
pthread_barrier_t barrier;
pthread_barrier_init(&barrier, NULL, THREAD_NUM);// Init the barrier with a number.
pthread_barrier_wait(&barrier);//The calling thread can continue only if there are
THREAD_NUM threads who call this method.
pthread_barrier_destroy(&barrier);
*/
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define THREAD_NUM 5
pthread_barrier_t barrier;
void travailler( )
{
int duree = ((int)random())%5+1;
int threadid=pthread_self();
printf("Pthread %d a une tâche de %d seconds.\n", threadid, duree);
int i = 0;
for(;i<duree;i++)
{
//If we put wait here, when a thread terminates, all others will be
//suspended, because there will nerver be enough threads who call wait()
//pthread_barrier_wait(&barrier);
printf("Pthread %d, second %d.\n", threadid, i);
sleep(1);
}
pthread_barrier_wait(&barrier);
printf("Pthread %d termine.\n", threadid);
}
void main()
{
int val_return;
pthread_t ptrs[THREAD_NUM];
pthread_barrier_init(&barrier, NULL, THREAD_NUM);
int i = 0;
for(;i<THREAD_NUM;i++)
{
if( (val_return=pthread_create(ptrs+i, NULL, (void*)travailler, NULL)) != 0)
{
perror("pthread_create");exit(1);
}
}
for(i=0;i<THREAD_NUM;i++)
{
pthread_join( ptrs[i], NULL);//Si le proc termine, les threads sont aussi terminés
printf("Join thread %d .\n", i);
}
//pthread_barrier_destroy(&barrier);
}