线程--隶属于进程,是进程中的程序流。操作系统支持多进程,每个进程内部支持多线程。多线程并行(同时执行)代码。

进程--重量级的,每个进程都需要独立的内存空间。

线程--轻量级的,线程不拥有独立的内存资源,共享所在进程的内存资源,

但每个线程都拥有一个独立的栈区。

开发程序中,多线程使用的概率更高。

进程中可以有多个线程,但必须有一个主线程(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编程之线程的更多相关文章

  1. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  2. .NET面试题解析(07)-多线程编程与线程同步

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实是很多的,比如多线程编程.线程上下文.异步编程.线程同步构造.GUI的跨线程访问等等, ...

  3. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  4. C#:异步编程和线程的使用(.NET 4.5 )

    摘自:http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N(葡萄城 ...

  5. 并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  6. Scala 深入浅出实战经典 第68讲:Scala并发编程原生线程Actor、Cass Class下的消息传递和偏函数实战解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  7. .NET面试题解析(07)-多线程编程与线程同步 (转)

    http://www.cnblogs.com/anding/p/5301754.html 系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实 ...

  8. vc 基于对话框多线程编程实例——线程之间的通信

     vc基于对话框多线程编程实例——线程之间的通信 实例:

  9. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

随机推荐

  1. 一个包的net到gs流程

    再来看看一个包走共享内存的流程 先来看看net进程这块如何处理的 {//用shareData这种类型封装刚才从无锁队列中取到的包 shareData sd; sd.channel_id = pkt.c ...

  2. JS语句循环(100以备奇偶数、100以内与7先关的数、100以内整数的和、10以内阶乘、乘法口诀、篮球弹起高度、64格子放东西)

    3.循环 循环是操作某一个功能(执行某段代码). ①循环四要素: a 循环初始值 b 循环的条件 c 循环状态 d 循环体 ②for循环 a 穷举:把所有的可能性的都一一列出来. b 迭代:每次循环都 ...

  3. sigaction 函数

    本文主要参考<unix环境高级编程>   sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作).   int sigaction(int signo,con ...

  4. SQL注入测试用例

    //看看是什么权限的and 1=(Select IS_MEMBER('db_owner'))And char(124)%2BCast(IS_MEMBER('db_owner') as varchar( ...

  5. PowerDesigner(九)-模型文档编辑器(生成项目文档)(转)

    模型文档编辑器 PowerDesigner的模型文档(Model  Report)是基于模型的,面向项目的概览文档,提供了灵活,丰富的模型文档编辑界面,实现了设计,修改和输出模型文档的全过程. 模型文 ...

  6. 异步任务(AsyncTask)

    1.Android UI组件更新简介 Android的UI线程主要负责处理用户的按键事件.用户触屏事件及屏幕绘图事件等,因此开发者的其它操作不应该,也不能阻塞UI线程,否则UI界面将会变的停止响应.A ...

  7. response ,request编码

    request.setCharacterEncoding()是你设置获得数据的编码方式.response.setCharacterEncoding()是你响应时设置的编码.response.setCo ...

  8. [译]如何使用 Docker 组件开发 Django 项目?

    原文地址:Django Development With Docker Compose and Machine 以下为译文 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包 ...

  9. POJ1474 Video Surveillance(半平面交)

    求多边形核的存在性,过了这题但是过不了另一题的,不知道是模板的问题还是什么,但是这个模板还是可以过绝大部分的题的... #pragma warning(disable:4996) #include & ...

  10. make_pair() (STL)

    转载来的 Pairs C++标准程序库中凡是“必须返回两个值”的函数, 也都会利用pair对象 class pair可以将两个值视为一个单元.容器类别map和multimap就是使用pairs来管理其 ...