创建多线程
线程可以访问全局变量
线程的局部存储
__thread int a;
a在线程局部存储,相当于每个线程都拷贝一份
__thread 局部存储只能用于内置类型,不能用于自定义类型
线程分离,结束后不用等待,自己释放资源
int pthread_detach(pthread_t thread);
线程分离可能导致进程不阻塞等待,提前结束,而使得线程也结束
线程分离可以父线程分离或者子线程分离
线程的互斥问题
多线程共同访问数据时,造成数据不一致问题。
模拟抢票代码
#include<iostream>
#include<vector>
#include<pthread.h>
#include<string>
#include<unistd.h>
using namespace std;
#define NUM 4
int tickets=1000;
// 用多线程 模拟一轮抢票
//代表一个线程
class threadData
{
public:
threadData(int number)
{
threadname="thread-"+to_string(number);
}
public:
string threadname;
};
void* getTicket(void* args)
{
threadData* td=static_cast<threadData*>(args);
while(true)
{
if(tickets>0)
{
usleep(1000);
printf("who=%s, get a ticket: %d\n",td->threadname.c_str(),tickets);
tickets--;
}
else
{
break;
}
}
return nullptr;
}
int main()
{
//线程tid
vector<pthread_t> tids;
// 线程数据
vector<threadData*> thread_datas;
for(int i=1;i<=NUM;i++)
{
pthread_t tid;
threadData* td=new threadData(i);
thread_datas.push_back(td);
// 传输数据,当扩展字段时可以直接扩展类变量
pthread_create(&tid,nullptr,getTicket,thread_datas[i-1]);
tids.push_back(tid);
}
for(auto thread:tids)
{
pthread_join(thread,nullptr);
}
for(auto td:thread_datas)
{
delete td;
}
return 0;
}
对一个全局变量进行变量进行多线程并发 --/++不是安全的。
寄存器不等于寄存器的内容
线程在执行时,将共享数据,加载到cpu的本质是把数据的内容,变成自己的上下文,给自己单独拿了一份。
--操作分为三步
数据从内存加载到寄存器
数据修改
数据从寄存器加载到内存
假设初始值是10,当线程1,处于第一步切换到线程2,3,4...,完成了三步,内存的值为1,但是又切换回线程1,线程恢复上下文的值为10,修改后内存的值又为9。
解决方法:对共享数据的访问,保证任何时候只有一个执行流
加锁的本质就是时间换资源,加锁的表现就是线程对于临界区代码的串行执行
纯互斥场景,如果锁分配不合理,容易导致其他线程的饥饿问题,竞争不到锁资源
解决方法:让所有的线程获取锁资源按照一定的顺序
不能立马重新申请锁,插入排队队列的末尾。
锁本身就是临界资源,申请锁和缩放锁是原子的
在线程被切出去,是持有锁被切走的。可以进行线程切换
全局变量定义锁:不用init和释放
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER
锁的原理
原子:一条汇编语句就是原子的
exchange或者swap指令能交换寄存器和内存内容
lock: movb $0 %al 将eax寄存器的值变空
xchgb %al, mutex 交换锁和寄存器的值
if(al 寄存器内容>0)
{
return 0;
} else 挂起等待
goto lock;
交换的本质是把数据1交换到线程的硬件上下文
只有一个1,多个0
movb $1 mutex 唤醒等待的mutex的线程
函数不可重入,多线程可能出现问题,可重入,不会出现多线程安全问题