Introduction détaillée à la programmation multithread en langage C sous Linux

黄舟
Libérer: 2017-10-14 11:00:42
original
2107 Les gens l'ont consulté

Cet article présente principalement la programmation multithread en langage C sous Linux. Les amis qui en ont besoin peuvent s'y référer

Lorsque nous écrivons des services Linux, nous utilisons souvent la technologie multithread Linux pour améliorer les performances du programme
Cet article présente principalement la programmation multithread en langage C sous Linux. 🎜>

Quelques conseils sur le multi-threading :

Une application peut démarrer plusieurs threads.

Thread (Lightweight Process, LWP) est la plus petite unité d'exécution de programme.

Généralement, le programme le plus simple aura au moins un thread, qui est le programme lui-même, c'est-à-dire la fonction principale (un processus monothread peut simplement être considéré comme un processus avec un seul thread)

Un fil de discussion est bloqué. Cela n'affectera pas les autres fils de discussion.

Les processus multithread peuvent utiliser autant que possible les ressources du processeur du système.

1 Créer un fil de discussion

Tout d'abord, commençons par un code simple qui crée un fil de discussion dans un processus, puis approfondissons.


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 printf("func run...\n");
 return NULL;
}
int main()
{
 pthread_t t1;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 sleep(1);
 return EXIT_SUCCESS;
}
int pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
Copier après la connexion

Dans la fonction principale, nous appelons la fonction ci-dessus pour créer un fil de discussion.

Paramètres de la fonction :

Le premier paramètre : pthread_t représente l'identifiant unique du thread créé. Après l'avoir créé, nous avons besoin du pointeur. est passé.

Le deuxième paramètre : pthread_attr_t, représente quelques configurations pour créer ce thread, comme la taille de la pile d'allocation, etc. . Généralement on peut renseigner NULL, qui représente la configuration de création de thread par défaut

Le troisième paramètre : représente l'adresse d'une fonction Lors de la création d'un thread, cette fonction sera appelée La valeur de retour de la fonction est void. *, les paramètres de la fonction. C'est aussi void*, le format général est comme void * func(void * arg){}

Le quatrième paramètre : représente le paramètre passé en appelant la troisième fonction

Valeur de retour de la fonction :

La fonction renvoie 0 avec succès Si elle n'est pas égale à 0, cela signifie que l'appel de fonction a échoué. À ce moment, strerror(errno) peut être utilisé pour imprimer le spécifique. erreur.

Remarque : chaque thread a une copie de errno, et différents threads ont un errno différent

Enfin compilé par gcc


gcc 1createthread.c -c -o 1createthread.o
gcc 1createthread.o -o thr1 -lpthread
Copier après la connexion

Lors de la compilation, vous devez ajouter -lpthread pour lier la bibliothèque dynamique libpthread.so, sinon cela vous demandera que la fonction est introuvable

L'appel de fonction renvoie le résultat

Question : Pourquoi la fonction sleep est-elle appelée , tous les threads se termineront ?

2 fils de discussion se bloquent

Parfois, nous créons un autre fil de discussion dans un fil de discussion, et le fil de discussion principal doit attendre que le fil de discussion créé revienne, le le thread principal ne se termine qu'après avoir obtenu la valeur de retour du thread. À ce stade, vous devez utiliser la suspension du fil.

La fonction pthread_join est utilisée pour suspendre le thread en cours jusqu'à ce que le thread spécifié par th se termine.

int pthread_join(pthread_t th, void **thr_return);。
Copier après la connexion

Résultat de l'exécution de la fonction

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d\n",*(int*)p);
 return EXIT_SUCCESS;
}
Copier après la connexion

Notre fonction principale attend que le thread créé termine son exécution, et obtient la valeur de retour de la fin de l'exécution du thread

3 Terminaison du thread

fonction exit() lorsque le processus se termine, alors qu'est-ce que c'est terminaison du fil ? Trois situations de terminaison de thread :

Le thread revient juste de la fonction de démarrage, et la valeur de retour est le code de sortie du thread.

Les discussions peuvent être annulées par d'autres discussions dans le même processus.

Le fil appelle pthread_exit.

Les paramètres de la fonction pthread_exit sont les mêmes que lorsque le thread normal se termine, et seront obtenus par le thread principal en attendant qu'il se termine.

Résultat de l'exécution de la fonction :
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 while(1)
 {
  if(i==10)
  {
   int * p = (int *)malloc(sizeof(int));
   *p=11;
   pthread_exit(p);
  }
  printf("fun run %d\n",i++);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d",*(int*)p);
 return EXIT_SUCCESS;
}
void pthread_exit(void *arg);
Copier après la connexion

Séparation à 4 threads

La fonction pthread_detach met le thread dans un état détaché.

Si vous n'attendez pas de thread et n'êtes pas intéressé par la valeur de retour du thread, vous pouvez définir le thread à l'état détaché et laisser le système recycler automatiquement les ressources qu'il occupe lorsque le thread se termine.
int pthread_detach(pthread_t th);
Copier après la connexion

Un thread ne peut pas appeler pthread_detach lui-même pour passer à l'état détaché. Il ne peut appeler pthread_detach que par d'autres threads.

Annulation de 5 threads

La fonction pthread_cancel permet à un thread d'en annuler un autre spécifié par le fil.

La fonction renvoie 0 en cas de succès, sinon elle renvoie non-0.
int pthread_cancel(pthread_t th);
Copier après la connexion

Résultat de l'exécution de la fonction :

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func1(void * arg)
{
 while(1)
 {
  printf("fun run...\n");
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1;
 if(pthread_create(&t1,NULL,func1,NULL)!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
  return -1;
 }
 sleep(5);
 pthread_cancel(t1);
 pthread_join(t1,NULL);
 return EXIT_SUCCESS;
}
Copier après la connexion

上面我们说过创建一个线程函数pthread_create的第二个参数,用来决定创建线程的一些初始化状态,这里我们 举个例子,改线程一创建就是分离状态的线程(

上面介绍了pthread_detach函数的概念,可以通过pthread_attr_t在创建线程的时候就指定线程属性为detach,而不用创建以后再去修改线程属性。

先上一段代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1;
 pthread_attr_t attr;//申明一个attr的结构体
 pthread_attr_init(&attr);//初始化结构体
 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置线程为分离线程
 int err = pthread_create(&t1,&attr,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 pthread_attr_destroy(&attr);
 pthread_join(t1,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
Copier après la connexion

pthread_attr_t就是我们要传入的参数的结构体,一般申明的步骤有

1,申明一个pthread_attr_t对象

2,函数pthread_attr_init初始化attr结构。

3,设置线程的一些属性,比如pthread_attr_setdetachstate函数就是设置该线程创建的时候为正常状态还是分离状态。

4,函数pthread_attr_destroy释放attr内存空间

pthread_attr_setdetachstate把线程属性设置为下面两个合法值之一:

说明

PTHREAD_CREATE_DETACHED

设置线程为分离状态

PTHREAD_CREATE_JOINABLE

设置线程为正常状态

上面函数运行结果:

因为线程是个分离状态的,所以pthread_join挂起会失效,主线程很快运行结束,程序也就结束了,创建的线程还没来得及运行

线程同步

有时候我们多个线程处理订单扣减库存会遇到这样的问题,两个线程同时进入一段代码先查询库存,两个都查出来为还剩一件库存,第一个线程用掉这个库存后,将库存变为0,但是第二个线程刚才也查出来为1了,所以他还认为有库存,

这个时候操作就会引发我们想不到的意外,库存变为负数了!!所以这个时候就需要使用线程的同步!!

先上一段代码看看效果:


#include<pthread.h>
#include<stdio.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
Copier après la connexion

函数运行结果:

可以看到两个线程是没有规律的争相处理的,如果这段代码是扣减库存就完蛋啦!,所以我们要对这段代码进行加锁,同一时刻只能有一个线程进入操作!

先上代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * func(void * arg)
{
 pthread_mutex_lock(&mutex);//对mutex加锁,其他线程进入后将会挂起,知道这个锁被解锁
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 pthread_mutex_unlock(&mutex);
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
Copier après la connexion

函数运行结果:

可以看到第二个线程先进入后一直运行结束,对mutex解锁后,第一个线程才能进方法里面运行!否则会挂起,一直等到锁被解锁!

PTHREAD_MUTEX_INITIALIZER是初始化一个快速锁的宏定义。


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Copier après la connexion

加锁解锁函数:


int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Copier après la connexion

总结

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!