线程
创建两个线程
makefile(添加原生线程库)
mythread:thread.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f mythread
thread.cc
#include <iostream>
#include <pthread.h>
#include<unistd.h>
using namespace std;
void *threadRun1(void *args)
{
while(true)
{
sleep(1);
cout << "t1 thread..." << getpid() << endl;
}
}
void *threadRun2(void *args)
{
while(true)
{
sleep(1);
cout << "t2 thread..." << getpid() << endl;
}
}
int main()
{
pthread_t t1, t2, t3;
// 取t地址,线程属性,线程执行的函数,后面说
pthread_create(&t1, nullptr, threadRun1, nullptr);
pthread_create(&t2, nullptr, threadRun2, nullptr);
while(true)
{
sleep(1);
cout << "main thread..." << getpid() << endl;
}
}
1 健壮性降低(多线程的特点)
- 有一个线程出问题,导致整个进程全部出问题
thread.cc
void *threadRun2(void *args)
{
char *s = "hello";
while(true)
{
sleep(1);
cout << "t2 thread..." << getpid() << endl;
*s = 'H'; //修改常量,让这个线程奔溃
}
}
显示(发生段错误导致整个进程崩溃)
- 系统角度:线程是进程的执行分支,线程干了,就是进程干了
- 信号角度:页表转换的时候,MMU识别写入权限的时候,没有验证通过,MMU发生异常问题,就被操作系统识别,因为信号是已进程为主的,他就要给进程发信号,向每个进程都写了异常,最后每个pcb都会被终止,操作系统就把进程整体全干掉了
2 缺乏访问控制(多线程的特点)
虚拟地址经过页表映射,你自己的代码和数据,包括操作系统的系统调用接口,系统的代码,各种数据全部在内存里放着,你要找到物理内存就必须要经过虚拟地址转化,那就尴尬了,因为执行流看到的资源都是通过地址空间看到的,多个LWP看到的是同一个地址空间,所以所有的线程可能会共享进程的大部分资源,因为地址空间是一样的,页表是一样的,把代码分了,但是未来写一个函数会被所有线程访问,定义一个全局变量,多个线程可能都会访问到,下面验证一下
thread.cc(定义一个全局变量,线程1和主线程负责打印,线程2负责修改)
int g_val = 0; //全局变量,在多线程场景中,多个线程看到的是同一个变量
void *threadRun1(void *args)
{
while(true)
{
sleep(1);
cout << "t1 thread..." << getpid() << "g_val: " << &g_val << endl;
}
}
void *threadRun2(void *args)
{
//char *s = "hello";
while(true)
{
sleep(1);
cout << "t2 thread..." << getpid() << "g_val: " << &g_val++ << endl;
//*s = 'H'; //修改常量,让这个线程奔溃
}
}
int main()
{
pthread_t t1, t2, t3;
// 取t地址,线程属性,线程执行的函数,后面说
pthread_create(&t1, nullptr, threadRun1, nullptr);
pthread_create(&t2, nullptr, threadRun2, nullptr);
while(true)
{
sleep(1);
cout << "main thread..." << getpid() << "g_val: " << &g_val << endl;
}
}
- 一个发生改变会影响整体