linuxbingc(多线程)
Posted 月屯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linuxbingc(多线程)相关的知识,希望对你有一定的参考价值。
目录标题
线程概念
曾经写的代码当中有没有线程呢
理解以前:
进程在内核当中就是一个task_struct,在该结构体当中的成员变量 pid被我们称之为进程号。
现在理解:
1.操作系统当中没有线程的概念,程序猿说的创建线程,本质上在1inux操作系统当中创建轻量级进程(lwp)所以轻量级进程等价线程。
有的!,曾经写的代码当中存在线程,就是执行mian函数的执行流,被称之为主线程;程序员创建的线程被称之为叫做“工作线程”
2. pid本质上是轻量级进程id,换句话说,就是线程ID
在task struct当中
pid_t pid; //轻量级进程id,也被称之为线程id,不同的线程拥有不同的pid
pid_t tgid; //轻量级进程组id,也被称之为进程id,一个进程当中的线程拥有相同的tgid
为什么进程概念的时候,说pid就是进程id?
因为主线程的pid和tgid相等
线程的共享与独有
独有:在进程虚拟地址空间的共享区当中
调用栈,寄存器,线程ID,errno,信号屏蔽字,调度优先级
共享:
文件描述符表,用户id,用户组id,信号处理方式,当前进程的工作目录
线程的优缺点:
优点:
- 多线程的程序,拥有多个执行流,“合理使用,可以提高程序的运行效率多线
- 程程序的线程切换比多进程程序快,付出的代价小(有些可以共享的数
据(全局变量)就能在线程切换的时候,不进行切换) - 可以充分发挥多核CPU并行的优势
计算密集型的程序,可以进行拆分,让不同的线程执行计算不一样的
事情(1+2+3+……)
I/0密集型的程序,可以进行拆分,让不同的线程执行不同的I/0操
作,可以不用串型运行,提高程序运行效率
缺点: - 编写代码的难度更加高
- 代码的(稳定性)鲁棒性要求更加高线程数量并不是越多越好(滑稽吃鸡)
- 缺乏访问控制,可能会导致程序产生二义性结果
- 一个线程崩溃,会导致整个进程退出。(滑稽吃鸡)
线程控制
g++ $^ -o $@ -lpthread -g
线程创建
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* m_thread_start(void* arg)
int* a=(int*)arg;
printf("i am woker%d\\n",*a);
int main()
pthread_t tid;
int i=5;
int ret=pthread_create(&tid,NULL,m_thread_start,(void*)&i);
printf("%d\\n",ret);
sleep(5);
return 0;
改善
struct Data_i
int i;
;
void* my_thread_start(void* arg)
struct Data_i* p = (struct Data_i*)arg;
while(1)
printf("i am work thread!: %d\\n", p->i);
sleep(1);
delete p;
int main()
for(int i = 0; i < 5; i++)
struct Data_i* lp = new Data_i();
lp->i = i;
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, (void*)lp);
if(ret < 0)
perror("pthread_create");
return 0;
while(1)
printf("i am main thread\\n");
sleep(1);
查看线程
ps aux | grep my
pstack 进程号
top -H -p 进程号
结论
测试入口函数的传参
结论1:不要传递临时变量给线程的入口函数
结论2:如果给线程入口函数传递了一个从堆上开辟的空间,让线程自行释放
线程终止
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* start(void* arg)
while(1)
printf("i am child");
sleep(1);
int main()
pthread_t tid;
//创建线程
int ret=pthread_create(&tid,NULL,start,NULL);
if(ret<0)
perror("pthread_create error");
return 0;
//线程中止
pthread_cancel(tid);
//主线程不退出
while(1)
sleep(5);
return 0;
获取线程标识符
void* my_thread_start(void* arg)
int count = 10;
while(count--)
printf("i am work thread\\n");
sleep(1);
pthread_cancel(pthread_self());
while(1)
printf("i am work thread-2\\n");
sleep(1);
int main()
//1.创建工作线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
//3.主线程死循环不退出, 观察现象
while(1)
printf("i am main thread\\n");
sleep(1);
return 0;
线程等待
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* start(void* arg)
int count=0;
while(count<10)
printf("i am child");
sleep(1);
count++;
return NULL;
int main()
pthread_t tid;
//创建线程
int ret=pthread_create(&tid,NULL,start,NULL);
if(ret<0)
perror("pthread_create error");
return 0;
pthread_join(tid,NULL);
return 0;
线程分离
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* my_thread_start(void* arg)
pthread_detach(pthread_self());
(void)arg;
int count = 50;
while(count--)
printf("i am work thread\\n");
sleep(1);
return NULL;
int main()
//1.创建工作线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
while(1)
sleep(1);
return 0;
线程不安全状态
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_ticket = 100000;
void* my_thread_start(void* arg)
//修改全局变量
while(g_ticket > 0)
printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
g_ticket--;
return NULL;
int main()
//1.创建线程
// 两个工作线程修改全局变量
pthread_t tid[2];
for(int i = 0; i < 2; i++)
int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
//2. 主线程 (只要不退出就好)
for(int i = 0; i < 2; i++)
pthread_join(tid[i], NULL);
return 0;
同步与互斥
互斥
互斥的要做的事情:控制线程的访问时序。当多个线程能够同时访问到临界资源的时候,有可能会导致线程执行的结果产生二义性。
而互斥就是要保证多个线程在访问同一个临界资源,执行临界区代码的时候(非原子性性操作(线程可以被打断)),控制访问时序。让一个线程独占临界资源执行完,再让另外一个独占执行;
互斥锁
原理
互斥锁的本质就是0/1计数器,计数器的取值只能为0或者1
计数器的值为1∶表示当前线程可以获取到互斥锁,从而去访问临界资源计数器的值为0︰表示当前线程不可以获取到互斥锁,从而不能访问临界资源
需要理解的是:并不是说线程不获取互斥锁不能访问临界资源,而是程序猿需要在代码当中用同一个互斥锁,去约束多个线程。
否则线程A加锁访问,线程B访问临界资源之前不加锁,那也约束不了线程B
信号量的计数器当中如何保证原子性
为什么计数器当中的值从0变成1,或者从1变成0是原子性的呢?
直接使用寄存器当中的值和计数器内存的值交换,而交换是一条汇编指令就可以完成的
加锁的时候:寄存器当中的值设置为(0)
第一种情况:计数器的值为1,说明锁空闲,没有被线程加锁
交换情况(画图)
第二种情况:计数器的值为0,说明锁忙碌,被其他线程加锁拿走
交换情况(画图)
解锁的时候:寄存器当中的值设置为(1)
计数器的值为0,需要解锁,进行一步交换。
问题
1.什么是线程不安全
- 多个线程并发/并行运行的时候,会导致程序结果的二义性。
- 假设有两个线程,线程A,线程B,有一个CPU,两个线程同时想对全局变量i进行加加,i的初始值为10;
2.怎么解决(互斥锁)
加锁
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_ticket = 100;
pthread_mutex_t g_lock;
void* my_thread_start(void* arg)
//修改全局变量
pthread_mutex_lock(&g_lock);
while(g_ticket > 0)
printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
g_ticket--;
return NULL;
int main()
//0.初始化互斥锁
pthread_mutex_init(&g_lock, NULL);
//1.创建线程
// 两个工作线程修改全局变量
pthread_t tid[2];
for(int i = 0; i < 2; i++)
int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
//2. 主线程 (只要不退出就好)
for(int i = 0; i < 2; i++)
pthread_join(tid[i], NULL);
return 0;
解锁
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_ticket = 100;
pthread_mutex_t g_lock;
void* my_thread_start(void* arg)
//修改全局变量
while(g_ticket>0)
pthread_mutex_lock(&g_lock);
if(g_ticket>0)
printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
g_ticket--;
pthread_mutex_unlock(&g_lock);
return NULL;
int main()
//0.初始化互斥锁
pthread_mutex_init(&g_lock, NULL);
//1.创建线程
// 两个工作线程修改全局变量
int i;
pthread_t tid[2];
for( i= 0; i < 2; i++)
int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
//2. 主线程 (只要不退出就好)
for( i = 0; i < 2; i++)
pthread_join(tid[i], NULL);
return 0;
销毁锁
同步
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define THREAD_COUNT 1
//代表碗: 0表示没有面, 1表示有面
int g_bowl = 0;
pthread_mutex_t g_lock;
void* eat_thread_start(void* arg)
while(1)
pthread_mutex_lock(&g_lock);
if(g_bowl == 0)
printf("我是吃面人, 碗里面没有面了, 我就不吃了....\\n");
pthread_mutex_unlock(&g_lock);
continue;
printf("碗里面有面, 我可以吃 : %d\\n", g_bowl--);
//printf("i am eat thread: eat %d\\n", g_bowl--);
pthread_mutex_unlock(&g_lock);
void* make_thread_start(void* arg)
while(1)
pthread_mutex_lock(&g_lock);
if(g_bowl == 1)
printf("我是做面人, 碗里面有面呢, 我就不做了...\\n");
pthread_mutex_unlock(&g_lock);
continue;
printf("碗里面没有面了, 我可以做面了, %d\\n", ++g_bowl);
//printf("i am make thread, make %d\\n", ++g_bowl);
pthread_mutex_unlock(&g_lock);
int main()
//1.初始化互斥锁
pthread_mutex_init(&g_lock, NULL);
//2.创建吃面的线程 和 做面的线程
pthread_t eat[THREAD_COUNT], make[THREAD_COUNT];
for(int i = 0; i < THREAD_COUNT; i++)
int ret = pthread_create(&eat[i], NULL, eat_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
ret = pthread_create(&make[i], NULL, make_thread_start, NULL);
if(ret < 0)
perror("pthread_create");
return 0;
//3.等以上是关于linuxbingc(多线程)的主要内容,如果未能解决你的问题,请参考以下文章