linux 线程编程详解
(它自己的指令计数器和cpu时钟)和各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP以及减小(进程/线程)上下文切换开销。
无论按照怎样的分法,一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等),而将线程分配到某个cpu上执行。一个进程当然可以拥有多个线程,此时,如果进程运行在SMP机器上,它就可以同时使用多个cpu来执行各个线程,达到最大程度的并行,以提高效率;同时,即使是在单cpu的机器上,采用多线程模型来设计程序,正如当年采用多进程模型代替单进程模型一样,使设计更简洁、功能更完备,程序的执行效率也更高,例如采用多个线程响应多个输入,而此时多线程模型所实现的功能实际上也可以用多进程模型来实现,而与后者相比,线程的上下文切换开销就比进程要小多了,从语义上来说,同时响应多个输入这样的功能,实际上就是共享了除cpu以外的所有资源的。
sudo apt-get install manpages-posix manpages-posix-dev
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
//四个参数意义
pthread_t *thread: 用来得到所创建的线程的ID,所以要创建变量,传入地址。
pthread_attr_t *attr: 线程的属性结构体变量地址,如果不特别指定属性,可以使用默认属性NULL。
start_routine: 线程函数,线程函数一般一个不退出的死循环。
arg:这是Linux系统在调用线程函数时传入的参数。
Compile and link with -pthread.
The new thread terminates(终止) in one of the following ways:
* It calls pthread_exit(3), specifying(指定) an exit status value that is
available to another thread in the same process that calls
pthread_join().
* It returns from start_routine(). This is equivalent(等效) to calling
pthread_exit(3) with the value supplied in the return statement.
* It is canceled (see pthread_cancel(3)).
* Any of the threads in the process calls exit(), or the main thread
performs a return from main(). This causes the termination of all
threads in the process.
由于线程共享进程的资源和地址空间,在对这些资源进行操作时,就必须要考虑到线程间资源访问的同步与互斥问题
有两种机制:互斥锁和信号量,互斥锁适用于可用资源是唯一的情况,信号量适用于可用资源为多个的情况
互斥锁的基本函数:
1、pthread_mutex_init():互斥锁初始化
2、pthread_mutex_lock():互斥锁上锁
3、pthread_mutex_trylock():互斥锁判断上锁
pthread_mutex_trylock()调用在参数mutex指定的mutex对象当前被锁住的时候立即返回,除此之外,pthread_mutex_trylock()跟pthread_mutex_lock()功能完全一样。
4、pthread_mutex_unlock():互斥锁解锁
5、pthread_mutex_destory():消除互斥锁
- 在主线程中初始化锁为解锁状态
- pthread_mutex_t mutex;
- pthread_mutex_init(&mutex, NULL);
- 在编译时初始化锁为解锁状态
- 锁初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 访问对象时的加锁操作与解锁操作
- 加锁 pthread_mutex_lock(&mutex)
- 释放锁 pthread_mutex_unlock(&mutex)
锁保护的并不是我们的共享变量(或者说是共享内存),对于共享的内存而言,用户是无法直接对其保护的,因为那是物理内存,无法阻止其他程序的代码访问。事实上,锁之所以对关键区域进行了保护,在本例中,是因为所有线程都遵循了一个规则,那就是在进入关键区域钱加
同一把锁,在退出关键区域钱释放同一把锁
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> int sharedi = ;
void increse_num(void); pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int main(){
int ret;
pthread_t thrd1, thrd2, thrd3; ret = pthread_create(&thrd1, NULL, (void *)increse_num, NULL);
ret = pthread_create(&thrd2, NULL, (void *)increse_num, NULL);
ret = pthread_create(&thrd3, NULL, (void *)increse_num, NULL); pthread_join(thrd1, NULL);
pthread_join(thrd2, NULL);
pthread_join(thrd3, NULL); printf("sharedi = %d\n", sharedi); return ; } void increse_num(void) {
long i,tmp;
for(i=; i<=; i++) {
/*加锁*/
if (pthread_mutex_lock(&mutex) != ) {
perror("pthread_mutex_lock");
exit(EXIT_FAILURE);
}
tmp = sharedi;
tmp = tmp + ;
sharedi = tmp;
/*解锁锁*/
if (pthread_mutex_unlock(&mutex) != ) {
perror("pthread_mutex_unlock");
exit(EXIT_FAILURE);
}
}
}
锁有一个很明显的缺点,那就是它只有两种状态:锁定与不锁定。
信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实,我们是可以把锁当作一个0-1信号量的。
它们是在/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数
相关函数
在使用semaphore之前,我们需要先引入头文件#include <semaphore.h>
- 初始化信号量:
int sem_init(sem_t *sem, int pshared, unsigned int value);- 成功返回0,失败返回-1
- 参数
- sem:指向信号量结构的一个指针
- pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
- value:信号量的初始值
- 信号量减1操作,当sem=0的时候该函数会堵塞
int sem_wait(sem_t *sem);- 成功返回0,失败返回-1
- 参数
- sem:指向信号量的一个指针
- 信号量加1操作
int sem_post(sem_t *sem);- 参数与返回同上
- 销毁信号量
int sem_destroy(sem_t *sem); - 参数与返回同上
/*************************************************************************
> File Name: sem.c
> Author: couldtt(fyby)
> Mail: fuyunbiyi@gmail.com
> Created Time: 2013年12月15日 星期日 19时25分08秒
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h> #define MAXSIZE 10 int stack[MAXSIZE];
int size = ;
sem_t sem; // 生产者
void provide_data(void) {
int i;
for (i=; i< MAXSIZE; i++) {
stack[i] = i;
sem_post(&sem); //为信号量加1
}
} // 消费者
void handle_data(void) {
int i;
while((i = size++) < MAXSIZE) {
sem_wait(&sem);
printf("乘法: %d X %d = %d\n", stack[i], stack[i], stack[i]*stack[i]);
sleep();
}
} int main(void) { pthread_t provider, handler; sem_init(&sem, , ); //信号量初始化
pthread_create(&provider, NULL, (void *)handle_data, NULL);
pthread_create(&handler, NULL, (void *)provide_data, NULL);
pthread_join(provider, NULL);
pthread_join(handler, NULL);
sem_destroy(&sem); //销毁信号量 return ;
}- 线程的系统调用1.pthread_createNAMEpthread_create - create a new threadSYNOPSIS#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);Compile and link with -pthread.2.pthread_exit(3)3.pthread_join(3)4.pthread_cancel(3)5.pthread_attr_init(3)//属性初始化函数#include <pthread.h>int pthread_attr_init(pthread_attr_t *attr);int pthread_attr_destroy(pthread_attr_t *attr);Compile and link with -pthread.6.pthread_detach()//设置线程分离属性7.pthread_self()
在这里谢谢汇文宋老师的指导,还有他的教学视频(www.huiwen.com).
linux 线程编程详解的更多相关文章
- Linux串口编程详解(转)
串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...
- Linux 网络编程详解九
TCP/IP协议中SIGPIPE信号产生原因 .假设客户端socket套接字close(),会给服务器发送字节段FIN: .服务器接收到FIN,但是没有调用close(),因为socket有缓存区,所 ...
- TCP/UDP Linux网络编程详解
本文主要记录TCP/UDP网络编程的基础知识,采用TCP/UDP实现宿主机和目标机之间的网络通信. 内容目录 1. 目标2.Linux网络编程基础2.1 嵌套字2.2 端口2.3 网络地址2.3.1 ...
- Linux 网络编程详解一(IP套接字结构体、网络字节序,地址转换函数)
IPv4套接字地址结构 struct sockaddr_in { uint8_t sinlen;(4个字节) sa_family_t sin_family;(4个字节) in_port_t sin_p ...
- 嵌入式 Linux线程锁详解pthread_mutexattr_t【转】
转自:http://blog.sina.com.cn/s/blog_8795b0970101il6g.html 在Posix Thread中定义有一套专门用于线程同步的mutex函数. . 创建和销毁 ...
- Linux 网络编程详解十
select int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *tim ...
- Linux 网络编程详解二(socket创建流程、多进程版)
netstat -na | grep " --查看TCP/IP协议连接状态 //socket编程提高版--服务器 #include <stdio.h> #include < ...
- Linux 网络编程详解十二
UDP的特点 --无连接 --基于消息的数据传输服务 --不可靠 --UDP更加高效 UDP注意点 --UDP报文可能会丢失,重复 --UDP报文可能会乱序 --UDP缺乏流量控制(UDP缓冲区写满之 ...
- Linux 网络编程详解十一
/** * read_timeout - 读超时检测函数,不含读操作 * @fd:文件描述符 * @wait_seconds:等待超时秒数,如果为0表示不检测超时 * 成功返回0,失败返回-1,超时返 ...
随机推荐
- 每一个程序员都应该知道的高并发处理技巧、创业公司如何解决高并发问题、互联网高并发问题解决思路、caoz大神多年经验总结分享
本文来源于caoz梦呓公众号高并发专辑,以图形化.松耦合的方式,对互联网高并发问题做了详细解读与分析,"技术在短期内被高估,而在长期中又被低估",而不同的场景和人员成本又导致了巨头 ...
- C#调用PB写的com组件dll
背景 小编为了使用C#去模仿PB代码的加密算法,结果发现PB算法中,的long类型只有21亿,实际上传入的数值达到了78亿,造成了数据溢出,精度丢失的情况. 然而PB的算法已经使用C#不可以还原(C# ...
- Struts2学习笔记④
刚才看书发现了一个问题,就是ActionSupport和Action接口的区别没搞清楚,弄得我以为我之前的代码写错了.其实ActionSupport已经实现了Action接口了,实际开发中也很少使用A ...
- SDWebImage下载图片的使用
第一步,下载SDWebImage,导入工程.github托管地址https://github.com/rs/SDWebImage 第二步,在需要的地方导入头文件 1 #import "UII ...
- java实体属性对应mysql和SQL Server 和Oracle 数据类型对应
1:Java数据类型与MySql数据类型对照表 类型名称 显示长度 数据库类型 JAVA类型 JDBC类型索引(int) VARCHAR L+N VARCHAR java.lang.String 12 ...
- 解决ecshop进入后台服务器出现500的问题
ecshop安装完成以后,前台页面打开正常,但是后台页面大家会出现500错误,看了很多的论坛和网站,删除过top.htm里面的JS代码的,.htaccess文件的修改的,都没有解决,后来找到原因, 原 ...
- iOS开发之数据存储之NSData
1.概述 使用archiveRootObject:toFile:方法可以将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象. NSDat ...
- 当Node.js遇见Docker
Node.js Best Practices - How to Become a Better Developer in 2017提到的几点,我们Fundebug深有同感: 使用ES6 使用Promi ...
- Tcl与Design Compiler (七)——环境、设计规则和面积约束
本文属于原创手打(有参考文献),如果有错,欢迎留言更正:此外,转载请标明出处 http://www.cnblogs.com/IClearner/ ,作者:IC_learner 本文的主要内容是讲解( ...
- java类的equals()函数和hashCode()函数用法
以前总觉得java类对象很简单,但是今天的一个同事的点播,让我对java的对象有了不一样的理解,下面我来介绍一下equals()和hashCode()的用法: 先粘一段代码: public class ...