>  기사  >  운영 및 유지보수  >  Linux에서 C 언어로 멀티스레드 프로그래밍에 대한 자세한 소개

Linux에서 C 언어로 멀티스레드 프로그래밍에 대한 자세한 소개

黄舟
黄舟원래의
2017-10-14 11:00:422110검색

이 글은 주로 Linux에서 C 언어로 된 멀티스레딩 프로그래밍을 소개합니다. 필요한 친구들은 참고하면 됩니다

우리는 Linux 서비스를 작성할 때 프로그램 성능을 향상시키기 위해 Linux 멀티스레딩 기술을 자주 사용합니다

약간 멀티스레딩에 대한 지식:

애플리케이션은 여러 스레드를 시작할 수 있습니다.

스레드(Lightweight Process, LWP)는 프로그램 실행의 가장 작은 단위입니다.

일반적으로 가장 간단한 프로그램에는 하나 이상의 스레드, 즉 프로그램 자체, 즉 주요 기능이 있습니다(단일 스레드 프로세스는 간단히 말해서 스레드가 하나만 있는 프로세스라고 생각하면 됩니다)

스레드 하나 차단 다른 스레드에는 영향을 미치지 않습니다.

멀티 스레드 프로세스는 시스템 CPU 리소스를 최대한 활용할 수 있습니다.

1 스레드 생성

먼저 프로세스에서 스레드를 생성하는 간단한 코드부터 시작한 다음 더 자세히 살펴보겠습니다.


#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);

메인 함수에서 위 함수를 호출하여 스레드를 생성합니다.

함수 매개변수:

첫 번째 매개변수인 pthread_t는 생성된 스레드의 고유 식별자를 나타냅니다. 이는 스레드를 생성한 후 이 구조의 포인터를 전달해야 합니다.

 두 번째 매개변수인 pthread_attr_t는 할당 스택 크기 등과 같이 이 스레드를 생성하기 위한 일부 구성을 나타냅니다. . 일반적으로 스레드 생성의 기본 구성을 나타내는 NULL을 채울 수 있습니다. 세 번째 매개변수는 함수의 주소를 나타냅니다. 스레드를 생성할 때 이 함수의 반환 값은 void*입니다. 함수의 매개변수도 void*입니다. 일반적인 형식은

void * func(void * arg){}과 같습니다. 네 번째 매개변수: 세 번째 함수를 호출하여 전달된 매개변수를 나타냅니다.

함수 반환 값:

함수는 성공적으로 0을 반환합니다. 가 0이 아닌 경우 함수 호출이 실패했음을 의미합니다. 이 경우 strerror(errno)를 사용하여 특정 오류를 인쇄할 수 있습니다.

참고: 각 스레드에는 errno의 복사본이 있고 다른 스레드에는 다른 errno가 있습니다

마지막으로 gcc를 통해 컴파일

gcc 1createthread.c -c -o 1createthread.o
gcc 1createthread.o -o thr1 -lpthread

컴파일할 때 libpthread.so 동적 라이브러리를 연결하려면 -lpthread를 추가해야 합니다. 함수를 찾을 수 없다는 메시지가 표시됩니다

함수 호출은 결과를 반환합니다

질문: 절전 함수가 호출되는 이유답: 새로 생성된 스레드가 해당 스레드에 도달하기 전에 메인 스레드가 종료될 수도 있습니다. 인쇄 방법. 기본 스레드가 종료되면 모든 스레드가 종료됩니다.

2 스레드 정지때때로 스레드에 또 다른 스레드를 생성하는데, 메인 스레드는 생성된 스레드가 반환될 때까지 기다려야 하며, 그런 다음 스레드의 반환 값을 얻은 후 메인 스레드가 종료됩니다. 이때 스레드 서스펜션을 사용해야 합니다.

int pthread_join(pthread_t th, void **thr_return);。

pthread_join 함수는 th로 지정된 스레드가 종료될 때까지 현재 스레드를 일시 중단하는 데 사용됩니다.

#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;
}

함수 실행 결과

우리의 메인 함수는 생성된 스레드의 실행이 완료되기를 기다리고 있었고, 스레드 실행 종료의 반환값을 받았습니다

3 스레드 종료 프로세스가 () 함수를 종료하면 종료됩니다. 그러면 스레드 종료란 무엇입니까?

스레드 종료의 세 가지 상황:

스레드는 시작 함수에서 반환되며 반환 값은 스레드의 종료 코드입니다.

스레드는 동일한 프로세스의 다른 스레드에 의해 취소될 수 있습니다.

스레드가 pthread_exit를 호출합니다.

#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);

pthread_exit 함수의 매개변수는 일반 스레드가 리턴을 종료할 때와 동일하며, 종료를 기다리는 메인 스레드에 의해 획득됩니다.

함수 연산 결과:

4 스레드 분리

int pthread_detach(pthread_t th);

pthread_detach 함수는 스레드를 분리된 상태로 만듭니다.

스레드를 기다리지 않고 스레드의 반환 값에 관심이 없다면 스레드를 분리된 상태로 설정하고 스레드가 종료될 때 시스템이 차지하는 리소스를 자동으로 재활용하도록 할 수 있습니다.

스레드는 분리된 상태로 변경하기 위해 pthread_detach 자체를 호출할 수 없습니다. 다른 스레드에서만 pthread_detach를 호출할 수 있습니다.

5 스레드 취소

int pthread_cancel(pthread_t th);

pthread_cancel 함수를 사용하면 한 스레드가 th로 지정된 다른 스레드를 취소할 수 있습니다.

함수가 성공하면 0을 반환하고, 그렇지 않으면 0이 아닌 값을 반환합니다.

#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;
}

함수 실행 결과:

上面我们说过创建一个线程函数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;
}

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;
}

函数运行结果:

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

先上代码:


#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;
}

函数运行结果:

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

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


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

加锁解锁函数:


int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

总结

위 내용은 Linux에서 C 언어로 멀티스레드 프로그래밍에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.