Linux多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux 下pthread的实现是通过系统调用clone()来实现的。clone()是 Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的 多线程程序 pthread_create.c。
一个重要的线程创建函数原型:
#include
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立线程返回0,否则返回错误的编号
形式参数:
pthread_t *restrict tidp 要创建的线程的线程id指针
const pthread_attr_t *restrict attr 创建线程时的线程属性
void* (start_rtn)(void) 返回值是void类型的指针函数
void *restrict arg start_rtn的行参
例程1:
功能:创建一个简单的线程
程序名称:pthread_create.c
代码如下:
#include
#include
void *mythread1(void)
{
int i;
for(i = 0; i < 10; i++)
{
printf("This is the 1st pthread,created by xiaoqiang!\n");
sleep(1);
}
}
void *mythread2(void)
{
int i;
for(i = 0; i < 10; i++)
{
printf("This is the 2st pthread,created by xiaoqiang!\n");
sleep(1);
}
}
int main(int argc, const char *argv[])
{
int i = 0;
int ret = 0;
pthread_t id1,id2;
ret = pthread_create(&id1, NULL, (void *)mythread1,NULL);
if(ret)
{
printf("Create pthread error!\n");
return 1;
}
ret = pthread_create(&id2, NULL, (void *)mythread2,NULL);
if(ret)
{
printf("Create pthread error!\n");
return 1;
}
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ vi thread1.c
fs@ubuntu:~/qiang/thread$ gcc -o thread1 thread1.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread1
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 2st pthread,created by xiaoqiang!
This is the 1st pthread,created by xiaoqiang!
fs@ubuntu:~/qiang/thread$
两个线程交替执行。
另外,因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread,否则编译不过,会出现下面错误
thread_test.c: 在函数 ‘create’ 中:
thread_test.c:7: 警告: 在有返回值的函数中,程序流程到达函数尾
/tmp/ccOBJmuD.o: In function `main':thread_test.c:(.text+0x4f):对‘pthread_create’未定义的引用
collect2: ld 返回 1
此例子介绍了创建线程的方法
下面例子介绍向线程传递参数。
例程2:
功能:向新的线程传递整形值
程序名称:pthread_int.c
代码如下:
#include
#include
void *create(void *arg)
{
int *num;
num = (int *)arg;
printf("Create parameter is %d\n",*num);
return (void *)0;
}
int main(int argc, const char *argv[])
{
pthread_t id1;
int error;
int test = 4;
int *attr = &test;
error = pthread_create(&id1,NULL,create,(void *)attr);
if(error)
{
printf("Pthread_create is not created!\n");
return -1;
}
sleep(1);
printf("Pthread_create is created..\n");
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ vi thread2.c
fs@ubuntu:~/qiang/thread$ gcc -o thread2 thread2.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread2
Create parameter is 4
Pthread_create is created..
fs@ubuntu:~/qiang/thread$
例程总结:
可以看出来,我们在main函数中传递的整行指针,传递到我们新建的线程函数中。
在上面的例子可以看出来我们向新的线程传入了另一个线程的int数据,线程之间还可以传递字符串或是更复杂的数据结构。
例程3:
程序功能:向新建的线程传递字符串
程序名称:pthread_string.c
代码如下:
#include
#include
void *create(char *arg)
{
char *str;
str = arg;
printf("The parameter passed from main is %s\n",str);
return (void *)0;
}
int main()
{
int error;
pthread_t id1;
char *str1 = "Hello ,xiaoqiang!";
char *attr = str1;
error = pthread_create(&id1, NULL, create, (void *)attr);
if(error != 0)
{
printf("This pthread is not created!\n");
return -1;
}
sleep(1);
printf("pthread is created..\n");
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ ./thread3
The parameter passed from main is Hello ,xiaoqiang!
pthread is created..
fs@ubuntu:~/qiang/thread$
例程总结:
可以看出来main函数中的字符串传入了新建的线程中。
例程4:
程序功能:向新建的线程传递字符串
程序名称:pthread_struct.c
代码如下:
#include
#include
#include
struct menber
{
int a;
char *s;
};
void *create(void *arg)
{
struct menber *temp;
temp = (struct menber *)arg;
printf("menber->a = %d\n",temp->a);
printf("menber->s = %s\n",temp->s);
return (void *)0;
}
int main()
{
int error;
pthread_t id1;
struct menber *p;
p = (struct menber *)malloc(sizeof(struct menber));
p->a = 1;
p->s = "xiaoqiang!";
error = pthread_create(&id1,NULL,create,(void *)p);
if(error)
{
printf("pthread is not created!\n");
return -1;
}
sleep(1);
printf("pthread is created!\n");
free(p);
p = NULL;
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ vi thread4.c
fs@ubuntu:~/qiang/thread$ gcc -o thread4 thread4.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread4
menber->a = 1
menber->s = xiaoqiang!
pthread is created!
fs@ubuntu:~/qiang/thread$
例程总结:
可以看出来main函数中的一个结构体传入了新建的线程中。
线程包含了标识进程内执行环境必须的信息。他集成了进程中的所有信息都是对线程进行共享的,包括文本程序、程序的全局内存和堆内存、栈以及文件描述符
例程5:
程序目的:验证新建立的线程可以共享进程中的数据
程序名称:pthread_share.c
代码如下:
#include
#include
static int a = 5;
void *create(void *arg)
{
printf("New pthread...\n");
printf("a = %d\n",a);
return (void *)0;
}
int main(int argc, const char *argv[])
{
int error;
pthread_t id1;
error = pthread_create(&id1, NULL, create, NULL);
if(error != 0)
{
printf("new thread is not created!\n");
return -1;
}
sleep(1);
printf("New thread is created...\n");
return 0;
}
结果如下:
fs@ubuntu:~/qiang/thread$ vi thread5.c
fs@ubuntu:~/qiang/thread$ gcc -o thread5 thread5.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread5
New pthread...
a = 5
New thread is created...
fs@ubuntu:~/qiang/thread$
例程总结:
可以看出来,我们在主线程更改了我们的全局变量a的值的时候,我们新建立的线程则打印出来了改变的值,可以看出可以访问线程所在进程中的数据信息。
2、线程的终止
如果进程中任何一个线程中调用exit,_Exit,或者是_exit,那么整个进程就会终止,
与此类似,如果信号的默认的动作是终止进程,那么,把该信号发送到线程会终止进程。
线程的正常退出的方式:
(1) 线程只是从启动例程中返回,返回值是线程中的退出码
(2) 线程可以被另一个进程进行终止
(3) 线程自己调用pthread_exit函数
两个重要的函数原型:
include
void pthread_exit(void *rval_ptr);
/*rval_ptr 线程退出返回的指针*/
int pthread_join(pthread_t thread,void **rval_ptr);
/*成功结束进程为0,否则为错误编码*/
pthread_join使一个线程等待另一个线程结束。
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
头文件 : #include
函数定义: int pthread_join(pthread_t thread, void **retval);
描述 :pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
参数 :thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
返回值 : 0代表成功。 失败,返回的则是错误号。
例程6
程序目的:线程正常退出,接受线程退出的返回码
程序名称:pthread_exit.c
执行代码如下:
#include
#include
#include
void *create(void *arg)
{
printf("new thread is created ... \n");
return (void *)0;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
void *temp;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("thread is not created ... \n");
return -1;
}
error = pthread_join(tid, &temp);
if( error )
{
printf("thread is not exit ... \n");
return -2;
}
printf("thread is exit code %d \n", (int )temp);
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ vi thread6.c
fs@ubuntu:~/qiang/thread$ gcc -o thread6 thread6.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread6
new thread is created ...
thread is exit code 0
fs@ubuntu:~/qiang/thread$
例程总结:
可以看出来,线程退出可以返回线程的int数值。
线程退出不仅仅可以返回线程的int数值,还可以返回一个复杂的数据结构
例程7
程序目的:线程结束返回一个复杂的数据结构
代码如下:
#include
#include
#include
struct menber
{
int a;
char *b;
}temp={8,"xiaoqiang"};
void *create(void *arg)
{
printf("new thread ... \n");
return (void *)&temp;
}
int main(int argc,char *argv[])
{
int error;
pthread_t tid;
struct menber *c;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("new thread is not created ... \n");
return -1;
}
printf("main ... \n");
error = pthread_join(tid,(void *)&c);
if( error )
{
printf("new thread is not exit ... \n");
return -2;
}
printf("c->a = %d \n",c->a);
printf("c->b = %s \n",c->b);
sleep(1);
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ gcc -o thread7 thread7.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread7
main ...
new thread ...
c->a = 8
c->b = xiaoqiang
fs@ubuntu:~/qiang/thread$
例程总结:
一定要记得返回的数据结构要是在这个数据要返回的结构没有释放的时候应用,如果数据结构已经发生变化,那返回的就不会是我们所需要的,而是脏数据。
3、线程标识
函数原型:
#include
pthread_t pthread_self(void);
pid_t getpid(void);
getpid()用来取得目前进程的进程识别码,函数说明
例程8
程序目的:实现在新建立的线程中打印该线程的id和进程id
代码如下:
#include
#include
#include /*getpid()*/
void *create(void *arg)
{
printf("New thread .... \n");
printf("This thread's id is %u \n", (unsigned int)pthread_self());
printf("The process pid is %d \n",getpid());
return (void *)0;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
printf("Main thread is starting ... \n");
error = pthread_create(&tid, NULL, create, NULL);
if(error)
{
printf("thread is not created ... \n");
return -1;
}
printf("The main process's pid is %d \n",getpid());
sleep(1);
return 0;
}
执行结果如下:
fs@ubuntu:~/qiang/thread$ gcc -o thread8 thread8.c -lpthread
fs@ubuntu:~/qiang/thread$ ./thread8
Main thread is starting ...
The main process's pid is 4955
New thread ....
This thread's id is 3075853120
The process pid is 4955
fs@ubuntu:~/qiang/thread$
最后提供一些参考资料
linux多线程编程
http://www.makeru.com.cn/course/details/1937?s=45051
循环链表及线性表的应用
http://www.makeru.com.cn/course/details/1902?s=45051
linux基础
http://www.makeru.com.cn/course/details/2058?s=45051
Linux多线程编程实例解析的更多相关文章
- Linux C语言多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- Linux多线程编程详细解析----条件变量 pthread_cond_t
Linux操作系统下的多线程编程详细解析----条件变量 1.初始化条件变量pthread_cond_init #include <pthread.h> int pthread_cond_ ...
- Linux网络编程实例解析
**************************************************************************************************** ...
- Linux 多线程编程实例
一.多线程 VS 多进程 和进程相比,线程有很多优势.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护代码段和数据.而运行于一个进程中的多个线程,他们之间使用相同 ...
- Linux 多线程编程 实例 2
编写一个程序,开启3个线程,这3个线程的ID分别为A.B.C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示:如:ABCABC….依次递推. 使用条件变量来实现: #inc ...
- Linux 多线程编程 实例 1
子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码. #include <pthread.h> ...
- linux下C语言多线程编程实例
用一个实例.来学习linux下C语言多线程编程实例. 代码目的:通过创建两个线程来实现对一个数的递加.代码: //包含的头文件 #include <pthread.h> #include ...
- Linux多线程编程初探
Linux线程介绍 进程与线程 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情.有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程 ...
- 【操作系统作业-lab4】 linux 多线程编程和调度器
linux多线程编程 参考:https://blog.csdn.net/weibo1230123/article/details/81410241 https://blog.csdn.net/skyr ...
随机推荐
- liquibase新增字段注释导致表格注释同时变更bug记录
liquibase是一个用于数据库变更跟踪.版本管理和自动部署的开源工具.它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍. 1. 问题复现 在使用过程中发现了一个版本bug.这个b ...
- JEECG代码审计之文件上传
JEECG代码审计之文件上传 0x01 简述 JEECG(J2EE Code Generation)是一款基于代码生成器JEE的智能开发平台.引领新的开发模式(Online Coding->代码 ...
- C# 下载远程http文件到本地
System.Windows.Forms.FolderBrowserDialog dialog = new System.Windows.Forms.FolderBrowserDialog(); ...
- nginx proxy_next_upstream 与openresty balancer.set_more_tries的使用
背景 我们这边网关服务使用的 kong,前段时间上线一个服务,这个服务它报错了,产生了502的错误码,追查这个报错的时候发现了网关服务的两个可疑的地方,第一个疑点是我们在Kong上配置的 Retrie ...
- Loadrunner拼装唯一值方法
由于Loadrunner函数有限性,唯一值需要几个函数的字符串进行拼装,可实现流水号.订单号等等数值的唯一性.具体可见下列方法: 方法一: char OraderID[15];srand(time{N ...
- 了解PHP-FPM
在服务器上,当我们查看php进程时,全都是php-fpm进程,大家都知道这个就是php的运行环境,那么,它到底是个什么东西呢? PHP-FPM简介 PHP-FPM,就是PHP的FastCGI管理器,用 ...
- 关于php的ini文件相关操作函数浅析
在小公司,特别是创业型公司,整个服务器的搭建一般也是我们 PHP 开发工程师的职责之一.其中,最主要的一项就是要配置好服务器的 php.ini 文件.一些参数会对服务器的性能产生深远的影响,而且也有些 ...
- 一起搞懂PHP的错误和异常(一)
在PHP的学习过程中,我们会接触到两个概念,一个是错误,一个是异常.啥玩意?他们不是一个东西嘛?如果接触过Java.C#之类的纯面向对象语言的同学,可能对异常是没有什么问题,毕竟所有的问题都可以try ...
- shell 一些常用命令
一般自己虚拟机新安装的centos才需要此配置 setenforce 是Linux的selinux防火墙配置命令 执行setenforce 0 表示关闭selinux防火墙. getenforce 查 ...
- genymotion启动模拟器后,sdk查询adb devices为空-解决方案
我们在genymotion中安装了一个安卓模拟器,比如Google Nexus 4,启动该模拟器后,在cmd中输入adb devices,发现为空. 解决方案:在genymotion选择Setting ...