Linux学习5-线程
线程
1.1什么是线程?
在一个程序中的多个执行路线就叫做线程(thread)。更准确的定义是:线程是一个进程内部的一个控制序列。
要搞清楚fork系统调用和创建新线程之间的区别。当进程执行fork调用时,将创建出该进程的一份新的副本。这个新进程拥有自己的变量和自己的PID,它的时间调度也是独立的,它的执行(通常)
几乎完全独立于父进程。当在进程中创建一个新线程时,新的执行线程将拥有自己的栈(因此也拥有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号处理函数和当前目录状态。
1.2第一个线程程序
线程有一套完整的相关的函数库调用,它们绝大多数以pthread_开头。为了使用这些函数调用,我们必须定义宏_REENTRANT,在程序中包含头文件pthread.h,并且在编译程序时需要使用选项-lpthread来链接线程库。
在一个多线程程序里,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据。
为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。
_REENTRANT为我们做三件事情,并且做的非常优雅:
(1)它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
(2)stdio.h中原来以宏的形式实现的一些函数将变成可安全重入函数。
(3)在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值。
举个例子便于更好理解可重入的重要性:
int g_val = ;//定义一个全局变量 void add()
{
g_val++;
}
如果有n个线程调用该函数,g_val是一个全局变量,如果加上-D _REENTRANT,则可重入,即g_val不会受其他线程的影响;但是没加-D _REENTRANT的时候,n-1个线程调用该函数时变量g_val就会彼此影响,出现不可预料的结果。
不会影响的原因是,加-D _REENTRANT后虽然调用的还是这个函数,但是不同的是该机制自动把上面的函数变成了
void add_r()
{
g_val++;
}
调用的函数不同了。
创建新线程的函数:
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
第一个参数thread指向pthred_t类型数据的指针。线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程。
第二个参数用于设置线程的属性。一般不需要特殊的属性,所以可以设为NULL。
第三个参数是一个函数地址,该函数以一个指向void的指针为参数,返回的也是一个指向void的指针。
用fork调用后,父子进程将在同一个位置继续执行下去,只是fork调用的返回值上不同的;
但对于新线程来说,我们必须明确地提供给它一个函数指针,新线程将在这个新位置开始执行。
第四个参数是要传递给上面函数(第三个参数)的参数。
该函数调用成功时返回值是0,如果失败则返回错误代码。
终止线程的函数:
#include <pthread.h>
void ptherad_exit(void *retval);
线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。该函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。
注意,绝对不能用它来返回一个指向局部变量的指针,因为线程在调用该函数后,这个局部变量就不再存在了,这将引起严重的程序漏洞。
等待线程的函数:
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
pthread_join函数在线程中的作用等价于进程中用来收集子进程信息的wait函数。
第一个参数指定了要等待的线程,线程通过pthread_create返回的标识符来指定。
第二个参数是一个指针,它指向了另一个指针,而后者指向了返回值。
这个函数成功返回0失败返回错误代码。
第一个线程示例代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *thread_function(void *arg);
char message[]="hello world!"; int main()
{
int res;
pthread_t a_thread;
void *thread_result; res = pthread_create(&a_thread,NULL,thread_function,(void *)&message);
if(res != )
{
perror("create thread is failed\n ");
exit(EXIT_FAILURE);
} printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread,&thread_result);
if (res!=)
{
perror("thread join failed!\n");
exit(EXIT_FAILURE); } printf("Thread joined,it return %s\n",(char *)thread_result);
printf("message now is %s\n",message);
exit(EXIT_SUCCESS); } void *thread_function(void *arg)
{
printf("th_func is running,Aragement was %s\n",(char *)arg);
sleep();
strcpy(message,"bye");
pthread_exit("Thanks for the CPU time\n"); }
执行结果:

编译这个程序的时候,我们首先需要定义宏_REENTRANT。在少数系统上,可能还需要定义宏_POSIX_C_SOURCE。(然而我并没有加也能实现功能,待解决)
接下来必须链接正确的线程库。
我系统默认的线程库是NPTL(查看头文件/usr/include/pthread.h 如果显示版权日期在2003年或更晚,那基本你的Linux发行版使用的是NPTL实现)

使用如下命令编译和链接:
cc -D_REENTRANT thread1.c -o thread1 -lpthread
ps:我把cc 换成 gcc 编译通过。
分析:main函数在创建新线程成功后,继续执行。然后执行pthread_join函数,一直等到它指定的线程终止才返回。然后主线程打印新线程返回值和全局变量后结束。
而新创建的线程将会执行thread_function。先打印自己的参数,睡眠5S后,更改全局变量最后退出时向主线程返回一个字符串。
Linux学习5-线程的更多相关文章
- linux 学习干货
学习了第七章. 每一个键盘对应一个信号.主要的有: ^代表 Ctrl <Backspance> erase ,删除一个字符. ^W werase,删除一个单词 ^U / ^X kill , ...
- Linux学习历程(持续更新整理中)
1.文件目录操作命令 (1) ls 显示文件和目录列表 a ls -l 显示文件的详细信息 b ls -a 列出当前目录的所有文件,包含隐藏文件. c stat '目录/文件' 显示指定目录 ...
- Linux学习总结(十四)—— 查看CPU信息
文章首发于[博客园-陈树义],点击跳转到原文Linux学习总结(十四)-- 查看CPU信息. Linux学习总结(十四)-- 查看CPU信息 商用服务器CPU最常用的是 Intel Xeon 系列,该 ...
- Linux多线程编程——线程的创建与退出
POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...
- Linux下查看线程数的几种方法汇总
Linux下查看线程数的几种方法汇总 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Linux下查看某个进程的线程数量 pstree命令以树状图显示进程间的关系(display ...
- Linux的进程线程及调度
本文为宋宝华<Linux的进程.线程以及调度>学习笔记. 1 进程概念 1.1 进程与线程的定义 操作系统中的经典定义: 进程:资源分配单位. 线程:调度单位. 操作系统中用PCB(Pro ...
- [转帖]Linux的进程线程及调度
Linux的进程线程及调度 本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10393707.html 本文为宋宝华<Linux的进程 ...
- 8)Linux程序设计入门--线程操作
)Linux程序设计入门--线程操作 前言:Linux下线程的创建 介绍在Linux下线程的创建和基本的使用. Linux下的线程是一个非常复杂的问题,由 于我对线程的学习不时很好,我在这里只是简单的 ...
- Linux学习路线指南
转载的,感觉写的挺好的,我自己知识复制了下,忘记了转载地址,抱歉! Linux学习路线指南 很多同学接触Linux不多,对Linux平台的开发更是一无所知.而现在的趋势越来越表明,作为一个优秀的软件开 ...
- python自动化开发学习 进程, 线程, 协程
python自动化开发学习 进程, 线程, 协程 前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...
随机推荐
- 复利计算测试(C语言)
对我们和复利计算程序,写单元测试. 有哪些场景? 期待的返回值 写测试程序. 运行测试. 测试模块 测试输入 预期结果 运行结果 bug跟踪 计算终值 (本金,年限,利率) 终值 1 (100 ...
- mysql数据优化--数据库结构的优化
1,比如存时间类型的就使用int类型 其中mysql的两个函数可以拿来使用 unix_timestamp 将时间日期转化为时间戳
- [转帖]Beyond Compare如何进行二进制比较
Beyond Compare如何进行二进制比较 http://www.beyondcompare.cc/jiqiao/erjinzhi-bijiao.html 在使用Beyond Compare软件比 ...
- 设置session的过期时间
1)修改php.ini文件中的gc_maxlifetime变量就可以延长session的过期时间了 session.gc_maxlifetime = 86400 然后,重启你的web服务(一般是apa ...
- VIM 命令收藏
1.vim#在命令行中输入vim,进入vim编辑器2.i#按一下i键,下端显示 --INSERT--#插入命令,在vim中可能任意字符都有作用3.Esc#退出i(插入)命令进行其它命令使用4.:r f ...
- Tomcat 总体结构
一.Tomcat 总体结构 1.Server(服务器)是Tomcat构成的顶级构成元素,所有一切均包含在Server中,Server的实现类StandardServer可以包含一个到多个Service ...
- MySQL复制 -- binlog(2)
MySQL复制是使用最为广泛的一套组建,上一节已经简单说了一下复制的一些用途和复制的原理,知道了这些我们能够快速的搭建起复制的平台,但是仅知道这些还是不够的,很多时候并不是一帆风顺的,总会有那么一小段 ...
- oracle 11g 从 dmp 文件中导出 sql 代码 的方法.
impdp sys/password full=y dumpfile=bg.dmp nologfile=y sqlfile=bg_dmp.sql 备注: bg.dmp 是 dmp 文件, bg_d ...
- 【BZOJ1922】大陆争霸(最短路)
[BZOJ1922]大陆争霸(最短路) 题面 BZOJ 洛谷 题解 最短路变形题. 定义\(dis\)表示最短路,\(d\)表示最早可以进入当前点的时间.显然\(d=max(max(dis_v,d_v ...
- 【ARC074e】RGB sequence
Description 一排\(n\)个格子,每个格子可以涂三种颜色的一种.现在给出\(m\)个形如"\([l,r]\)中必须恰好有\(x\)种颜色"的限制(\(1 \le l ...