UC编程之线程
线程--隶属于进程,是进程中的程序流。操作系统支持多进程,每个进程内部支持多线程。多线程并行(同时执行)代码。
进程--重量级的,每个进程都需要独立的内存空间。
线程--轻量级的,线程不拥有独立的内存资源,共享所在进程的内存资源,
但每个线程都拥有一个独立的栈区。
开发程序中,多线程使用的概率更高。
进程中可以有多个线程,但必须有一个主线程(main函数)。
由于CPU不可分,真正意义的并行其实不存在的,针对时间点的并行是不存在
的。人的感官都是针对时间段(很少)。主流的操作系统都是使用CPU时间片的
方式实现并行。每个CPU时间片都是极少的CPU运行时间。
多线程编程有一套成形的API,属于POSIX规范。
POSIX规范提供了一个头文件pthread.h 和一个共享库文件lib pthread.so。
因此在编写多线程代码时需要#include<pthread.h>,链接时需要加 -l pthread
或 -pthread
如何创建线程?
关于线程的大多数函数/成员变量都是以pthread_开头。
int pthread_create(pthread_t *id, const pthread_attr_t *attr,
void *(fa) (void *), void *p)
参数id用于存储新建线程的ID
attr用于传入新建线程的属性,一般0即可
fa是函数指针,线程执行的代码写成函数fa并在创建线程时传入
p是fa的参数,如果不需要0即可
注意:线程的错误处理不使用errno,而是直接返回。
当同一进程内部有多线程时,每个线程内部的代码顺序执行,而多线程之间的
代码乱序执行。
多线程之间 相互独立,但也相互影响。
主线程结束,进程随之结束;进程结束,进程中所有线程都结束。
pthread_join函数挂起当前线程直到所等待的线程结束。
线程的参数由 pthread_create的第4个参数传入,返回值由pthread_join的第二
个参数获取。
函数pthread_exit(void*)也可以退出线程并且也有返回值(参数void*就是线程的
返回值)
函数exit()退出的是进程(所有线程),pthread_exit()退出单个线程。
线程的运行有两种状态:
非分离状态--可以用函数pthread_join
分离状态 --可以用函数pthread_detach
两者的区别:
非分离状态的线程资源回收要到pthread_join函数结束以后;分离状态的线程资源马上回收。处于分离状态的线程无法join(join没效果)
经验:线程启动后,最好设置join或者detach
实例:
创建线程:
1 #include<stdio.h>
2 #include<pthread.h>
3
4 void* task(void* p){
5 int i;
6 for(i=0;i<100;i++){
7 printf("i=%d\n",i);
8 }
9 }
10
11
12 int main(){//主线程
13 pthread_t id;
14 pthread_create(&id,0,task,0);//创建线程
15 int i;
16 for(i=0;i<100;i++){
17 printf("main=%d\n",i);
18 }
19 sleep(1);
20 pthread_t id2 = pthread_self();//取当前线程
21 printf("id=%u,id2=%u\n",id);
22 return 0;
23 }
等待线程(pthread_join):
1 #include<stdio.h>
2 #include<pthread.h>
3
4 void* task(void* p){
5 int* pi = p;
6 printf("*pi=%d\n",*pi);
7 *pi =200;
8 }
9 int main(){//主线程
10 pthread_t id1;
11 int x =100;
12 pthread_create(&id1,0,task,&x);//创建线程
13 pthread_join(id1,0);//主线程等待id1的结束
14 printf("x=%d\n",x);
15 return 0;
16 }
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<pthread.h>
4
5 void* task(void* p){
6 printf("%d\n",(int)p);
7
8 }
9 void* task2(void* p){
10 sleep(1);
11 int* pi = p;
12 printf("*pi=%d\n",*pi);
13 }
14 int main(){
15 pthread_t id;
16 int x = 100;
17 pthread_create(&id,0,task,(void*)x);
18 pthread_join(id,0);
19 int* pi = malloc(4);
20 *pi=300;
21 pthread_create(&id,0,task2,pi);
22 free(pi);//free()释放掉了pi,使pi无效,传入task2将无效
23 pthread_join(id,0);
24 return 0;
25 }
线程的取消--线程可以被其他线程取消,有一套API(了解)
1 #include <stdio.h>
2 #include <pthread.h>
3 void* task1(void *p){//线程2取消线程1
4 //设置可以取消
5 pthread_setcancelstate(//换成DISABLE不能取消
6 PTHREAD_CANCEL_ENABLE,0);
7 //设置取消方式: 立即/下一个取消点
8 pthread_setcanceltype(//立即,DEFERRED下一个
9 PTHREAD_CANCEL_DEFERRED/*ASYNCHRONOUS*/,0);
10 while(1) {
11 printf("I am superman!\n");
12 usleep(1); } }
13 void* task2(void *p){
14 sleep(3); printf("开始取消线程1\n");
15 pthread_cancel(*(pthread_t*)p); }
16 int main(){
17 pthread_t id1,id2;
18 pthread_create(&id1,0,task1,0);
19 pthread_create(&id2,0,task2,&id1);
20 pthread_join(id1,0); pthread_join(id2,0);
21 return 0;
22 }
线程同步--因为多线程之间共享进程的资源,多个线程同时访问相同的资源时需要互相协调,以防止数据出现不一致、不完整的问题,线程之间的协调和通信叫线程同步。
线程同步有很多解决方案:
线程同步的思路:访问共享资源时,不能并行,而是串行(一个一个的访问)
pthread中,提供了互斥量(互斥锁)实现线程同步。
互斥量的使用步骤:
1 定义互斥量 pthread_mutex_t lock;
2 初始化互斥量,方法有二:
pthread_mutex_init(&lock,0);
或者在定义的时候用宏同时赋值:
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
3 加上互斥锁(给访问共享资源的代码加)
pthread_mutex_lock(&lock)
4 访问
5 解锁 pthread_mutex_unlock(&lock)
6 释放互斥锁的资源
pthread_mutex_destroy(&lock)
互斥量实例:
1 #include<stdio.h>
2 #include<pthread.h>
3
4 char* data[5];//存数据
5 int size = 0; //人数
6
7 pthread_mutex_t lock; //1 定义
8 //= PTHREAD_MUTEX_INITIALIZER;
9
10 void* task(void* p){
11 pthread_mutex_lock(&lock);//3 上锁
12 data[size] = (char*)p;
13 usleep(100000);
14 size++;
15 pthread_mutex_unlock(&lock);//4 解锁
16 }
17
18 int main(){
19 pthread_mutex_init(&lock,0);//2 初始化
20 data[size] = "zhangfei";
21 size++;
22 pthread_t id1,id2;
23 pthread_create(&id1,0,task,"guanyu");
24 pthread_create(&id2,0,task,"zhaoyun");
25 pthread_join(id1,0);
26 pthread_join(id2,0);
27 int i;
28 for(i=0;i<size;i++){
29 printf("%s\n",data[i]);
30 }
31 pthread_mutex_destroy(&lock);
32 return 0;
33 }
信号量-- 就是一个计数器,控制同时访问共享资源的线程/进程数量。
如果信号量为1,效果等同于互斥量。
信号量的使用,已经有了固定的API,步骤:
1 定义一个信号量(semaphore)
sem_t sem;
信号量不属于pthread.h
2 初始化信号量
sem_init(&sem,0,最大值);
第二个参数必须是0,0代表控制线程,其他值代表控制进程,Linux只支持线程。
第三个参数就是计数的初始值,为1信号量的作用等价于互斥量。
3 获取一个信号量(信号量减1)
sem_wait(&sem);
4 访问共享资源
5 释放一个信号量(信号量加1)
sem_post(&sem)
6 如果不再使用,可以删除信号量
sem_destroy(&sem)
信号量实现线程同步实例:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<semaphore.h>
4
5 char* data[5];//存数据
6 int size = 0; //人数
7
8 sem_t sem; //1 定义
9
10 void* task(void* p){
11 sem_wait(&sem);//3 上锁
12 data[size] = (char*)p;
13 usleep(100000);
14 size++;
15 sem_post(&sem);//4 解锁
16 }
17
18 int main(){
19 sem_init(&sem,0,1);//2 初始化
20 data[size] = "zhangfei";
21 size++;
22 pthread_t id1,id2;
23 pthread_create(&id1,0,task,"guanyu");
24 pthread_create(&id2,0,task,"zhaoyun");
25 pthread_join(id1,0);
26 pthread_join(id2,0);
27 int i;
28 for(i=0;i<size;i++){
29 printf("%s\n",data[i]);
30 }
31 sem_destroy(&sem);
32 return 0;
33 }
练习:
控制访问数据库的最大的并行线程数量
数据库最多运行10个线程同时访问,启动20个线程,看一下信号量控制效果
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<semaphore.h>
4
5 sem_t sem;
6 void* task(void* p){
7 int i =(int)p;
8 printf("第%d个线程启动,申请连接\n",i);
9 sem_wait(&sem);
10 printf("第%d个线程申请成功\n",i);
11 sleep(10);
12 printf("第%d个线程完成连接\n",i);
13 sem_post(&sem);
14 }
15 int main(){
16 sem_init(&sem,0,10);//初始化
17 int i;
18 for(i = 0;i<20;i++){
19 pthread_t id;
20 pthread_create(&id,0,task,(void*)(i+1));
21 }
22 while(1);
23 // sem_destroy(&sem);
24 return 0;
25 }
死锁:线程之间互相锁定,导致所有线程都无法运行。多线程一定要避免锁死
避免死锁的经验:
顺序上锁,反向解锁,不要回调。
thread1:
lock(&mutex1);
...//1
lock(&mutex2);//3 thread1阻塞等待mutex2
...
unlock(&mutex2);
...
unlock(&mutex1);
thread2:
lock(&mutex2);//2
...//4
lock(&mutex1);//5 thread2阻塞等待mutex1
...
unlock(&mutex1);
...
unlock(&mutex2);
thread1运行到lock(&mutex2)时,如果thread2已经运行了lock(&mutex2)则thread1将阻塞等待thread2,thread2运行到lock(&mutex1);时,因为thread1已经运行了lock(&mutex1);thread2将阻塞等待thread1,结构thread1和thread2都将永远等待对方,无法结束,形成死锁
UC编程之线程的更多相关文章
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- .NET面试题解析(07)-多线程编程与线程同步
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实是很多的,比如多线程编程.线程上下文.异步编程.线程同步构造.GUI的跨线程访问等等, ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- C#:异步编程和线程的使用(.NET 4.5 )
摘自:http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N(葡萄城 ...
- 并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- Scala 深入浅出实战经典 第68讲:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...
- .NET面试题解析(07)-多线程编程与线程同步 (转)
http://www.cnblogs.com/anding/p/5301754.html 系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实 ...
- vc 基于对话框多线程编程实例——线程之间的通信
vc基于对话框多线程编程实例——线程之间的通信 实例:
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
随机推荐
- 一个包的net到gs流程
再来看看一个包走共享内存的流程 先来看看net进程这块如何处理的 {//用shareData这种类型封装刚才从无锁队列中取到的包 shareData sd; sd.channel_id = pkt.c ...
- JS语句循环(100以备奇偶数、100以内与7先关的数、100以内整数的和、10以内阶乘、乘法口诀、篮球弹起高度、64格子放东西)
3.循环 循环是操作某一个功能(执行某段代码). ①循环四要素: a 循环初始值 b 循环的条件 c 循环状态 d 循环体 ②for循环 a 穷举:把所有的可能性的都一一列出来. b 迭代:每次循环都 ...
- sigaction 函数
本文主要参考<unix环境高级编程> sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作). int sigaction(int signo,con ...
- SQL注入测试用例
//看看是什么权限的and 1=(Select IS_MEMBER('db_owner'))And char(124)%2BCast(IS_MEMBER('db_owner') as varchar( ...
- PowerDesigner(九)-模型文档编辑器(生成项目文档)(转)
模型文档编辑器 PowerDesigner的模型文档(Model Report)是基于模型的,面向项目的概览文档,提供了灵活,丰富的模型文档编辑界面,实现了设计,修改和输出模型文档的全过程. 模型文 ...
- 异步任务(AsyncTask)
1.Android UI组件更新简介 Android的UI线程主要负责处理用户的按键事件.用户触屏事件及屏幕绘图事件等,因此开发者的其它操作不应该,也不能阻塞UI线程,否则UI界面将会变的停止响应.A ...
- response ,request编码
request.setCharacterEncoding()是你设置获得数据的编码方式.response.setCharacterEncoding()是你响应时设置的编码.response.setCo ...
- [译]如何使用 Docker 组件开发 Django 项目?
原文地址:Django Development With Docker Compose and Machine 以下为译文 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包 ...
- POJ1474 Video Surveillance(半平面交)
求多边形核的存在性,过了这题但是过不了另一题的,不知道是模板的问题还是什么,但是这个模板还是可以过绝大部分的题的... #pragma warning(disable:4996) #include & ...
- make_pair() (STL)
转载来的 Pairs C++标准程序库中凡是“必须返回两个值”的函数, 也都会利用pair对象 class pair可以将两个值视为一个单元.容器类别map和multimap就是使用pairs来管理其 ...