一、前言
首先来回顾一下cpu的随机轮转机制
- 顺序随机(固定优先级情况下)
- 时间随机(可能有固定时间,就像鸿蒙OS芯片是10毫秒)
- 次数随机
其次是进程与线程的区别
- 1、进程是资源分配的基本单位,线程是CPU调度的基本单位
- 2、一个进程中可以包含n条线程,n条线程同时共享当前进程的所有资源
- 3、进程无法共享数据(要通过IPC),在一个进程内多条线程可以共享数据
计算机会为一个进程开辟磁盘、内存和cpu等,用进程来计算,进程再开出线程,线程的开辟更加的节省资源开销,线程再共享当前进程的所有资源
区别可以按数据共享、开辟空间、创建上开销、应用场景上分点列举
二、线程同步概述
1、线程同步
概念:两个或多个线程同时进行时,经常需要访问到公共资源或代码的关键部分。
2、线程同步问题
概念:多个线程争抢CPU的执行权,同时访问同一片公共区域时产生的线程同步的数据不安全问题(例如:一个线程可能没有执行完,执行了一半就去执行另外一个线程)
原因:cpu随机轮片,执行时间随机
解决方法:使用线程互斥量或者信号量
3、用互斥量进行同步
- 程序员给某个对象加上一把“锁”,每次只允许一个线程去访问它
- 如果想对代码关键部分的访问进行控制,你必须在进入这段代码之前锁定一把互斥量,在完成操作后再打开它
4、互斥量的使用
使用步骤如下
(1)互斥量的声明(全局变量)
pthread_mutex_t mutex;//全局的锁
(2)互斥量的初始化
pthread_mutex_init(&mutex, NULL);
(3)互斥量的加锁与解锁
pthread_mutex_lock(&mutex);
cout << "子线程2号 student.name = " << data.name << endl;
cout << "子线程2号 student.sex = " << data.sex << endl;
pthread_mutex_unlock(&mutex);
sleep(1);
加锁与解锁的中间的部分是公共资源(全局变量、共享内存、单例设计等),也就是多个线程都要访问的部分。
示例
1、代码
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//线程执行函数 类似run
using namespace std;
// 一个进程中全局变量在多个线程中可以共享 number++ 可以进行叠加
int number = 0;
//互斥量 用来实现线程锁
pthread_mutex_t mutex;
typedef struct student
{
char name[10];
char sex[10];
}STU;
STU data = { "",""};
void* pthread_func(void *p)
{
//char *p1= (char*)p;
while (true)//两个while同时跑
{
/*number++;
cout << "子线程1号执行。。。。。。number = "<< number ++<< " p1 = " << p1 << endl;*/
pthread_mutex_lock(&mutex);
if (number % 2 == 0)
{
strcpy(data.name,"张三");
strcpy(data.sex, "男");
}
else
{
strcpy(data.name, "李四");
strcpy(data.sex, "女");
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void* pthread_func2(void* p)
{
while (true)//两个while同时跑
{
/*int* p2 = (int*)p;
number++;
cout << "子线程2号执行。。。。。。number = " << number++ << " p2 = " <<*p2 << endl;*/
pthread_mutex_lock(&mutex);
cout << "子线程2号 student.name = " << data.name << endl;
cout << "子线程2号 student.sex = " << data.sex << endl;
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_t thread_id;
pthread_t thread_id2;
char buf[15] = { "hello" };
int temp = 666;
pthread_mutex_init(&mutex, NULL);
if (pthread_create(&thread_id,NULL, pthread_func, NULL))
{
perror("pthread_create error");
}
if ( pthread_create(&thread_id2, NULL, pthread_func2, NULL))
{
perror("pthread_create error");
}
while (true)
{
cout << "主线程执行 number = " << number++ << endl;
sleep(1);
}
return 0;
}
2、运行结果
?
结果分析:
1、第一次打印的name和sex是空的,因为cpu调度线程的顺序随机,即使是线程1先开,线程2后开,但对于cpu来说他们两是公平竞争执行权的(不代表先写就先执行)。图中可以看出运行的时候先跑了主线程,之后立刻跑了线程2(打印),线程1还没执行(修改),所以导致打印的数据是空的
2、因为使用了互斥量,所以结果正常。如果不加锁解锁,因为cpu执行时间也随机,在拷贝完李四这个名字后(还没有做性别拷贝)就立刻被切换到线程2中执行,就打印了一个名字后又立刻被切换到线程1中拷贝性别,拷贝完又立刻? 被切换到线程2中打印性别,导致最后把李四改成男的
时间随机也就是进入为while循环里的代码还没有全部走完,就被切换成其他线程执行
总结:
1、为什么通过上锁就能解决同步问题? 本质原因是pthread_mutex_lock(&mutex)是一个阻塞式函数,只有遇到unlock后才会执行lock操作 例如:线程1执行到一半,此时线程2想切换成自己执行,因为上了锁,只有等到线程1执行了unlock 才能执行
2、为什么不能在锁中sleep (1) ? sleep 的机制是类似于切换线程了,这时候没有解锁,线程就进入死锁状态。
3、注意
- 不能有无法结束的死循环
- 操作部分应该是公共资源或者关键代码
- 操作完,必须解锁
?
|