首页 运维 linux运维 linux下C语言的多线程编程的详细介绍

linux下C语言的多线程编程的详细介绍

Oct 14, 2017 am 11:00 AM
linux 编程 详细

这篇文章主要介绍了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);
登录后复制

在main函数里面我们调用上面的函数进行创建一个线程。

函数参数:

  第一个参数: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
登录后复制

编译的时候需要加上-lpthread 用来链接libpthread.so动态库,不然会提示找不到function

函数调用返回结果

问题:为什么调用sleep函数

答:可能新创建的线程还没运行到打印的方法主线程就结束了,而主线程结束,所有线程都会结束了。

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线程终止

进程终止时exit()函数,那么线程终止是什么呢?

线程终止的三种情况:

线程只是从启动函数中返回,返回值是线程的退出码。

线程可以被同一进程中的其他线程取消。

线程调用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函数的参数就跟正常线程结束return的使用时一样的,都会被等待它结束的主线程获取到。

函数运行结果:

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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

linux怎么启动nginx linux怎么启动nginx Apr 14, 2025 pm 12:51 PM

在 Linux 中启动 Nginx 的步骤:检查 Nginx 是否已安装。使用 systemctl start nginx 启动 Nginx 服务。使用 systemctl enable nginx 启用在系统启动时自动启动 Nginx。使用 systemctl status nginx 验证启动是否成功。在 Web 浏览器中访问 http://localhost 查看默认欢迎页面。

怎么查看nginx是否启动 怎么查看nginx是否启动 Apr 14, 2025 pm 01:03 PM

确认 Nginx 是否启动的方法:1. 使用命令行:systemctl status nginx(Linux/Unix)、netstat -ano | findstr 80(Windows);2. 检查端口 80 是否开放;3. 查看系统日志中 Nginx 启动消息;4. 使用第三方工具,如 Nagios、Zabbix、Icinga。

linux怎么查看nginx是否启动 linux怎么查看nginx是否启动 Apr 14, 2025 pm 12:48 PM

在 Linux 中,使用以下命令检查 Nginx 是否已启动:systemctl status nginx根据命令输出进行判断:如果显示 "Active: active (running)",则 Nginx 已启动。如果显示 "Active: inactive (dead)",则 Nginx 已停止。

nginx403错误怎么解决 nginx403错误怎么解决 Apr 14, 2025 pm 12:54 PM

服务器无权访问所请求的资源,导致 nginx 403 错误。解决方法包括:检查文件权限。检查 .htaccess 配置。检查 nginx 配置。配置 SELinux 权限。检查防火墙规则。排除其他原因,如浏览器问题、服务器故障或其他可能的错误。

centos和ubuntu的区别 centos和ubuntu的区别 Apr 14, 2025 pm 09:09 PM

CentOS 和 Ubuntu 的关键差异在于:起源(CentOS 源自 Red Hat,面向企业;Ubuntu 源自 Debian,面向个人)、包管理(CentOS 使用 yum,注重稳定;Ubuntu 使用 apt,更新频率高)、支持周期(CentOS 提供 10 年支持,Ubuntu 提供 5 年 LTS 支持)、社区支持(CentOS 侧重稳定,Ubuntu 提供广泛教程和文档)、用途(CentOS 偏向服务器,Ubuntu 适用于服务器和桌面),其他差异包括安装精简度(CentOS 精

docker原理详解 docker原理详解 Apr 14, 2025 pm 11:57 PM

Docker利用Linux内核特性,提供高效、隔离的应用运行环境。其工作原理如下:1. 镜像作为只读模板,包含运行应用所需的一切;2. 联合文件系统(UnionFS)层叠多个文件系统,只存储差异部分,节省空间并加快速度;3. 守护进程管理镜像和容器,客户端用于交互;4. Namespaces和cgroups实现容器隔离和资源限制;5. 多种网络模式支持容器互联。理解这些核心概念,才能更好地利用Docker。

Centos停止维护2024 Centos停止维护2024 Apr 14, 2025 pm 08:39 PM

CentOS将于2024年停止维护,原因是其上游发行版RHEL 8已停止维护。该停更将影响CentOS 8系统,使其无法继续接收更新。用户应规划迁移,建议选项包括CentOS Stream、AlmaLinux和Rocky Linux,以保持系统安全和稳定。

nginx怎么启动 nginx怎么启动 Apr 14, 2025 pm 01:06 PM

问题:如何启动 Nginx?答案:安装 Nginx启动 Nginx验证 Nginx 是否已启动探索其他启动选项自动启动 Nginx

See all articles