目录
如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?
地址空间和页表
如何看待地址空间和页表
1、地址空间是进程能看到的资源窗口
2、页表决定,进程真正拥有资源的情况
3、合理的对地址空间+页表进行资源划分,我们就可以对一个进程所有的资源进行分类。
虚拟地址如何转化到物理地址的
- 选择虚拟地址的前10个比特位在页目录当中进行查找,找到对应的页表。
- 再选择虚拟地址的10个比特位在对应的页表当中进行查找,找到物理内存中对应页框的起始地址。
- 最后将虚拟地址中剩下的12个比特位作为偏移量从对应页框的起始地址处向后进行偏移,找到物理内存中某一个对应的字节数据。
物理内存因为OS也要对其做管理,对物理内存进行了分页
struct Page{ // 内存的属性-- 4KB
}
一个个小块的物理内存为页框。
线程与进程的关系
线程:进程内的一个执行流
linux中是没有真正的线程的。
如果我们OS真的要专门设计“线程”概念,OS未来要不要管理这个线程呢?
肯定是要管理的! 先描述再组织!
一定要为线程设计专门的数据结构表示线程对象,TCB。--- Windows就是这样做的
单纯从线程调度角度,线程和进程有很多地方是重叠的!
所以,我们的linux工程师,不想给linux“线程”专门设计对应的数据结构!而是直接服用PCB!用PCB来表示linux内部的“线程”。
什么叫进程?
内核视角:承担分配系统资源的基本实体。
什么叫线程?
CPU调度的基本单位!
如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?
1、linux内核中有没有真正意义的线程?没有的,linux是用进程PCB来模拟线程的,是一种完全属于自己的一套线程方案。
2、站在CPU的视角,每一个PCB,都可以称之为叫做轻量级进程
3、linux线程是CPU调度的基本单位,而进程是承担分配系统资源的基本单位
4、进程用来整体申请资源,线程用来伸手向进程要资源。
5、linux中没有真正意义的线程。
在进程中
状态、上下文切换、调度算法、包括进程与文件之间的对应关系、全部适用于线程!曾经我们为PCB编写的算法、数据结构是不是可以让线程复用并且可以复用进程的调度算法。
好处是什么?
简单维护成本大大降低 -- 可靠性高效!
windows线程与linux线程
linux无法提供创建线程的系统调用接口!而只能给我们提供创建轻量级进程的接口!
因为linux里面根本没有线程的概念,只有轻量级进程的。
但Windows里面有具体的线程,并且有对应的线程控制块TCB。
在Windows和Linux中,线程都是用于实现并发执行的基本单位。它们之间的主要区别在于线程的实现方式和调度机制。
在Windows中,线程是由操作系统内核来管理和调度的,因此线程的创建和销毁等操作都需要系统调用。
而在Linux中,线程是由用户空间的线程库来管理和调度的,因此线程的创建和销毁等操作都可以在用户空间中完成,不需要进行系统调用。
如何理解?举一个例子来说明!
家庭: 进程
家庭成员: 线程
代码示例:
#include <iostream>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
using namespace std;
// 新线程
void *thread_routine(void *args)
{
while(true)
{
cout<<"我是新线程,我正在运行!"<<endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
int n=pthread_create(&tid,0,thread_routine\
,(void*)"thread one");
assert(0==n);
(void)n;
// 主线程
while(true)
{
cout<<"我是主线程,我正在运行!"<<endl;
sleep(1);
}
return 0;
}
代码解释:
当kill 掉这个进程的时候,主线程和新线程都会被kill掉,因为kill是给进程发信号,当进程退出的时候会把对应的资源也给释放掉,因此所对应的线程不得不退出。
轻量级进程
查看轻量级进程的指令
ps -aL
轻量级进程ID
LWP:(light weight process)轻量级进程ID
PID与LWP
PID与LWP相等的是主线程
PID与LWP不相等的是新线程
CPU调度的时候,是以哪一个id为标识符表示特定一个执行流的呢?
LWP
当内部只有一个执行流的时候,内部只有一个主线程。也可以将我们传入的线程名字给打印出来。
代码如下:
// 新线程
void *thread_routine(void *args)
{
const char *name = (const char *)args;
while (true)
{
cout << "我是新线程,我正在运行!"
<< "name:" << name << endl;
sleep(1);
}
}
运行结果:
[mwb@VM-16-14-centos lesson1]$ ./mythread
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
我是主线程,我正在运行!
我是新线程,我正在运行!name:thread one
线程的概念
举个例子(见见猪跑)
线程一旦被创建,几乎所有的资源都是被所有的线程所共享的。
代码示例:
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <assert.h>
#include <unistd.h>
using namespace std;
int g_val = 0;
string fun()
{
return "我是一个独立的方法";
}
// 新线程
void *thread_routine(void *args)
{
const char *name = (const char *)args;
while (true)
{
cout << "我是新线程,我正在运行!"
<< "name:" << name << ": " << fun()<<" ";
cout << "g_val: " << g_val++ << "&g_val: " << &g_val << endl;
// fun();
sleep(1);
}
}
int main()
{
pthread_t tid;
int n = pthread_create(&tid, 0, thread_routine, (void *)"thread one");
assert(0 == n);
(void)n;
// 主线程
while (true)
{
char tidbuffer[64];
snprintf(tidbuffer, sizeof(tidbuffer), "0x%x", tid);
// cout << tidbuffer;
cout << "我是主线程,我正在运行!,我创建出来的线程tid: "\
<< tidbuffer << ": " << fun()<<" ";
cout << "g_val: " << g_val << "&g_val: " << &g_val << endl;
// printf("0x%x",tid);
fun();
sleep(1);
}
return 0;
}
运行结果:
[mwb@VM-16-14-centos lesson1]$ ./mythread
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 0&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 0&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 1&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 1&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 2&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 2&g_val: 0x6021f4
我是主线程,我正在运行!,我创建出来的线程tid: 0x9a4eb700: 我是一个独立的方法 g_val: 3&g_val: 0x6021f4
我是新线程,我正在运行!name:thread one: 我是一个独立的方法 g_val: 3&g_val: 0x6021f4
结论:
因此我们发现,两个线程共享了这个全局的函数。
并且同一个全局变量两个线程的地址一样,并且当有一个线程对该全局变量修改的时候,另一个线程也能看到
什么资源是线程私有的?
Linux中线程私有的资源包括线程ID、寄存器、栈、errno变量和线程私有数据等。
线程ID:是一个唯一标识符,用于区分不同的线程。
寄存器:是CPU中的一组存储器,用于存储当前线程的状态信息。栈:是用于存储函数调用和局部变量的一块内存区域,每个线程都有自己的栈。
errno变量:是用于存储系统调用错误码的全局变量,每个线程都有自己的errno变量。线程私有数据:是一种机制,用于在不同的线程之间共享数据,每个线程都有自己的一份私有数据。
什么是线程?
在一个程序里的一个执行路线就叫做线程(thread)。
更准确的定义是:线程是“一个进程内部的控制序列”。
一切进程至少都有一个执行线程。
线程在进程内部运行,本质是在进程地址空间内运行。
在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
cache刷新策略
由于cache中缓存了很多热点数据,线程共享这片区域,所以cache不用更新
进程切换是需要cache刷新的。这是因为进程切换会导致CPU缓存中的数据无效,因此需要切换cache以确保新进程能够访问正确的数据。这个过程被称为“cache刷新”。
一个线程如果出现了异常,会影响其他线程吗? 为什么?
会的,健壮性/鲁棒性较差
代码示例:
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;
void *start_routine(void *args)
{
// 一个线程如果出现了异常,会影响其他线程吗? 为什么?
// 会的,健壮性/鲁棒性较差
string name = static_cast<const char *>(args); // 安全的进行强制类型转化
while (true)
{
cout << "new thread create success, name: " << name << endl;
sleep(1);
int *p=nullptr;
*p=0;
}
}
int main()
{
pthread_t id;
pthread_create(&id, nullptr, start_routine, (void *)"thread one");
while (true)
{
cout << "new thread create success, name: main thread" << endl;
sleep(1);
}
}
运行结果:
[mwb@VM-16-14-centos lesson1]$ ./mythread
new thread create success, name: main thread
new thread create success, name: thread one
new thread create success, name: main thread
Segmentation fault
给线程发信号
给所有PID相同的线程写入11号信号,因此所有的线程全部退出了。
进程信号,信号是整体发给进程的!进程是承担系统分配资源的实体,当进程回收你赖以生存的资源的时候,线程肯定结束!
使用vfork函数
vfork创建一个子进程
创建出来的子进程是与父进程共享进程地址空间的。
也就相当于轻量级进程了。
线程控制
线程的创建
pthread_create函数
功能:
创建一个新的线程
函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
参数
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数,一般为线程名
返回值:成功返回0;失败返回错误码
错误码的解析:
传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回。
pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小。
创建多个线程
代码示例:
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <unistd.h>
using namespace std;
// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
// 线程id
pthread_t tid;
// 线程的名字
char namebuffer[64];
};
// 创建新线程
//1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
// 2. 该函数是可重入函数吗?
void *start_routine(void *args)
{
sleep(1);
// 一个线程如果出现了异常,会影响其他线程吗? 为什么?
// 会的,健壮性/鲁棒性较差
ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
int cnt = 10;
while (cnt)
{
cout << "new thread create success, name: " << td->namebuffer
<< "cnt: " << cnt-- << endl;
sleep(1);
}
delete td;
return nullptr; //线程结束的,return的时候,线程就终止了
}
int main()
{
// 1.我们想创建一批线程
vector<ThreadData *> threads;
#define NUM 10
for (int i = 0; i < NUM; i++)
{
ThreadData *td = new ThreadData();
snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i+1);
pthread_create(&td->tid, nullptr, start_routine, td);
threads.push_back(td);
// sleep(1);
}
for(auto &iter : threads)
{
cout<<"create thread: "<<iter->namebuffer<<" : "<<iter->tid<<" success"<<endl;
}
while (true)
{
cout << "new thread create success, name: main thread" << endl;
sleep(1);
}
return 0;
}
在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!
线程终止
如何终止
exit是不能来终止线程的,而是用来终止进程的
任何一个执行流调用exit都会让整个进程退出
因此应该调用phtread_exit
退出方法
1、return nullptr;
2、pthread_exit(nullptr);
线程的等待
概念
线程也是要被等待的!如果不等待,会造成类似僵尸进程的问题 -- 内存泄漏
线程必须也要被等待:
1、获取新线程的退出信息
2、回收新线程对应的PCB等内核资源,防止内存泄漏 -- 暂时无法查看!
那我们可以不关心退出信息吗?
可以。
但是我们是必须要回收资源的。
pthread_join函数
原型
int pthread_join(pthread_t thread,void **retval);
参数
retval :是一个输出型参数,用来获取线程函数结束时,返回的退出结果。
代码示例:目的是使用join来获取新线程退出后的退出信息
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include<assert.h>
#include <unistd.h>
using namespace std;
// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
// 线程id
pthread_t tid;
// 线程的名字
char namebuffer[64];
// 线程的编号
int number;
};
class ThreadReturn
{
public:
int exit_code;
int exit_result;
};
// 创建新线程
//1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
//2. 该函数是可重入函数吗?
//3.在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!
void *start_routine(void *args)
{
// sleep(1);
// 一个线程如果出现了异常,会影响其他线程吗? 为什么?
// 会的,健壮性/鲁棒性较差
ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
int cnt = 5;
while (cnt)
{
cout<<"cnt: "<<cnt<<" &cnt:"<<&cnt<<endl;
cnt--;
// cout << "new thread create success, name: " << td->namebuffer
// << "cnt: " << cnt-- << endl;
// pthread_exit(nullptr);
sleep(1);
}
ThreadReturn *tr=new ThreadReturn();
tr->exit_code=1;
tr->exit_result=106;
return (void*)tr;// 右值
}
int main()
{
// 1.我们想创建一批线程
vector<ThreadData *> threads;
#define NUM 10
for (int i = 0; i < NUM; i++)
{
ThreadData *td = new ThreadData();
// 用来记录线程的编号
td->number=i+1;
snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i+1);
pthread_create(&td->tid, nullptr, start_routine, td);
threads.push_back(td);
}
for(auto &iter : threads)
{
cout<<"create thread: "<<iter->namebuffer<<" : "<<iter->tid<<" success"<<endl;
}
for(auto &iter : threads)
{
ThreadReturn *ret=nullptr;
// void *ret=nullptr;// 注意:是void *
int n= pthread_join(iter->tid,(void**)&ret);
assert(0==n);
cout<<"join : "<<iter->namebuffer << " success,exit_code: "<<\
ret->exit_code<<", exit_result: "<<ret->exit_result<<endl;
delete iter;
}
cout<<"main thread quit "<<endl;
return 0;
}
为什么没有见到线程退出的时候,对应的退出信号??
线程出现异常,收到信号,整个进程都会退出!
pthread_join:默认就认为函数会调用成功!不考虑异常问题,异常问题是你进程该考虑的问题!
线程取消
概念
线程是可以被取消的!注意事项:线程要被取消,前提是这个线程已经跑起来了
取消也是线程终止的一种。
线程如果是被取消的,那么退出码为-1。
代码示例:线程的取消所使用函数:pthread_cancel
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <assert.h>
#include <unistd.h>
using namespace std;
// 创建一个结构体来存放线程的基本属性
class ThreadData
{
public:
// 线程id
pthread_t tid;
// 线程的名字
char namebuffer[64];
// 线程的编号
int number;
};
class ThreadReturn
{
public:
int exit_code;
int exit_result;
};
// 创建新线程
// 1. start_routine,现在是被几个线程执行呢? 10 ,这个函数是什么状态
// 2. 该函数是可重入函数吗?
// 3.在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用-- 在多线程情况下,也没有问题 -- 其实每一个线程都有自己独立的栈结构!
void *start_routine(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
int cnt = 5;
while (cnt)
{
cout << "cnt: " << cnt << " &cnt:" << &cnt << endl;
cnt--;
sleep(1);
}
ThreadReturn *tr = new ThreadReturn();
tr->exit_code = 1;
tr->exit_result = 106;
return (void *)tr; // 右值
}
int main()
{
// 1.我们想创建一批线程
vector<ThreadData *> threads;
#define NUM 10
for (int i = 0; i < NUM; i++)
{
ThreadData *td = new ThreadData();
// 用来记录线程的编号
td->number = i + 1;
snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%2d", "thread", i + 1);
pthread_create(&td->tid, nullptr, start_routine, td);
threads.push_back(td);
// sleep(1);
}
for (auto &iter : threads)
{
cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " success" << endl;
}
// 线程是可以被cancel取消的,注意:线程被取消,前提是这个线程已经跑起来了
// 线程如果是被取消的,那么退出码为-1
sleep(5);
for (int i=0;i<threads.size()/2;i++)
{
pthread_cancel(threads[i]->tid);
cout << "pthread_cancel : " << threads[i]->tid << " success" << endl;
}
for (auto &iter : threads)
{
void *ret = nullptr;
assert(0 == n);
cout << "join : " << iter->namebuffer << " success,exit_code: " <<(long long)ret<<endl;
delete iter;
}
cout << "main thread quit " << endl;
return 0;
}
在C++中创建线程
#include <iostream>
#include <thread>
#include <unistd.h>
void thread_run()
{
while (true)
{
std::cout << "我是新线程..." << std::endl;
sleep(1);
}
}
int main()
{
std::thread t1(thread_run);
while(true)
{
std::cout<<"我是主线程..."<<std::endl;
sleep(1);
}
t1.join();
return 0;
}
结论:
任何语言,在Linux中如果要实现多线程,必定是要用pthread库
如何看待C++中的多线程呢?C++11的多线程,在linux环境中,本质是对pthread库的封装!Linux中的线程称为用户级线程。
分离线程
线程是可以等待的、等待的时候,阻塞式等待。
如果我们不想等待呢?
分离线程
默认情况下,新创建的线程是joinable的,线程退出的时候,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄露。
如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
可以用线程分离来实现。
使用pthread_self()可以获取当前线程的id。
代码示例:
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
std::string changeId(const pthread_t &thread_id)
{
char tid[128];
snprintf(tid, sizeof(tid), "0x%x", thread_id);
return tid;
}
void *start_routine(void *args)
{
std::string threadname = static_cast<const char *>(args);
while (true)
{
char tid[128];
snprintf(tid, sizeof(tid), "0x%x", pthread_self());
std::cout << threadname << " running ... " << changeId(pthread_self()) << std::endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, start_routine, (void *)"thread one");
std::string main_id = changeId(pthread_self());
std::cout << "main running ... new thread id: " << changeId(tid) << "main thread id: " << main_id << std::endl;
pthread_join(tid, nullptr);
return 0;
}
代码结论:
可以获取主线程的线程id和新线程的线程id。
并且使用changeId来证明了我们获取新线程id的正确性。
pthread_detach函数
原型
int pthread_detach(pthread_t thread);
代码示例:
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <cstring>
std::string changeId(const pthread_t &thread_id)
{
char tid[128];
snprintf(tid, sizeof(tid), "0x%x", thread_id);
return tid;
}
void *start_routine(void *args)
{
std::string threadname = static_cast<const char *>(args);
int cnt=5;
while (cnt--)
{
std::cout << threadname << " running ... " << changeId(pthread_self()) << std::endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, start_routine, (void *)"thread one");
std::string main_id = changeId(pthread_self());
pthread_detach(tid);
std::cout << "main running ... new thread id: " << changeId(tid) << "main thread id: " << main_id << std::endl;
// sleep(5);
// 一个线程默认是joinable的,如果设置了分离状态,就不能进行等待了
int n = pthread_join(tid, nullptr);
std::cout<<"result : "<<n<<": "<<strerror(n)<<std::endl;
return 0;
}
原生线程库
概念
原生线程库中,可能要存在多个线程--你用这些接口创建了线程,别人可以同时再用吗?
要不要对线程进行管理呢?
要!
先描述(线程的属性,比较少!),再组织!
线程的属性:
线程id,独立栈,线程的局部存储。
Linux的方案:
用户级线程,用户关心的线程属性在库中,内核提供线程执行流的调度。
Linux用户级线程:内核轻量进程=1:1。
线程id是该线程在线程库中该线程TCB的地址。独立栈在共享区的库中。
栈的分布。
主线程是用的自己地址空间中的栈。
其他线程用的是共享区中库中的栈。
线程的局部存储:
// 添加__thread,可以将一个内置类型设置为线程局部存储
__thread int g_val = 100;可以让该变量放在共享区的栈上。
封装一个原生线程库
Thread.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <functional>
#include <cassert>
#include <cstring>
#include <string>
class Thread;
// 上下文
class Context
{
public:
Thread *this_;
void *args_;
public:
Context():this_(nullptr),args_(nullptr)
{}
~Context()
{}
};
class Thread
{
public:
typedef std::function<void *(void *)> func_t;
const int num = 1024;
public:
Thread(func_t func, void *args, int number)
: func_(func), args_(args)
{
// name_="thread-";
// name_+=std::to_string(number);
char buffer[num];
snprintf(buffer, sizeof buffer, "thread-%d", number);
name_ = buffer;
// 异常 == if : 意料之外用异常或者if判断
// assert:意料之中用assert
Context *ctx=new Context();
ctx->this_=this;
ctx->args_=args_;
int n = pthread_create(&tid_, nullptr, start_routine,ctx); // TODO
assert(n == 0);
(void)n;
// 编译debug的方式发布的时候是存在的,release方式发布,
// assert就不存在l,n就是一个定义,
// 但是没有使用的变量,有些编译器下会有warning
}
// 在类内创建线程,想让线程执行对应的方法,需要将方法设置成为static
// 类内成员,有缺省参数!在第一参数中包含了一个this指针
static void *start_routine(void *args)
{
Context *ctx=static_cast<Context*>(args);
void *ret=ctx->this_->run(ctx->args_);
delete ctx;
return ret;
// 静态方法不能调用成员方法或者成员变量
// return func_(args_);
}
void join()
{
int n = pthread_join(tid_, nullptr);
assert(n == 0);
(void)n;
}
void *run(void *args)
{
return func_(args);
}
~Thread()
{
// do northing
}
private:
std::string name_;
func_t func_;
void *args_;
pthread_t tid_;
};
mythread.cc
#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <memory>
#include <cstring>
#include "Thread.hpp"
void *thread_run(void *args)
{
std::string work_type=static_cast<const char*>(args);
while(true)
{
std::cout<<"我是一个新线程,我正在做:"<<work_type<<std::endl;
sleep(1);
}
}
int main()
{
std::unique_ptr<Thread> thread1(new Thread(thread_run,(void*)"hellothread",1));
std::unique_ptr<Thread> thread2(new Thread(thread_run,(void*)"countthread",2));
std::unique_ptr<Thread> thread3(new Thread(thread_run,(void*)"logthread",3));
thread1->join();
thread2->join();
thread3->join();
return 0;
}