目录
如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?
地址空间和页表
如何看待地址空间和页表
虚拟地址如何转化到物理地址的
物理内存因为OS也要对其做管理,对物理内存进行了分页
struct Page{ // 内存的属性-- 4KB
}
一个个小块的物理内存为页框。
线程与进程的关系
线程:进程内的一个执行流
linux中是没有真正的线程的。
如果我们OS真的要专门设计“线程”概念,OS未来要不要管理这个线程呢?
单纯从线程调度角度,线程和进程有很多地方是重叠的!
什么叫进程?
内核视角:承担分配系统资源的基本实体。
什么叫线程?
CPU调度的基本单位!
如何看待我们之前学习进程时,对应的进程概念呢?和今天的冲突吗?
在进程中
好处是什么?
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;
}
代码解释:
轻量级进程
查看轻量级进程的指令
轻量级进程ID
PID与LWP
CPU调度的时候,是以哪一个id为标识符表示特定一个执行流的呢?
当内部只有一个执行流的时候,内部只有一个主线程。也可以将我们传入的线程名字给打印出来。
代码如下:
// 新线程
void *thread_routine(void *args)
{
const char *name = (const char *)args;
while (true)
{
cout << "我是新线程,我正在运行!"
<< "name:" << name << endl;
sleep(1);
}
}
运行结果:
线程的概念
举个例子(见见猪跑)
线程一旦被创建,几乎所有的资源都是被所有的线程所共享的。
代码示例:
#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;
}
运行结果:
结论:
因此我们发现,两个线程共享了这个全局的函数。
并且同一个全局变量两个线程的地址一样,并且当有一个线程对该全局变量修改的时候,另一个线程也能看到
什么资源是线程私有的?
什么是线程?
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);
}
}
运行结果:
给线程发信号
使用vfork函数
线程控制
线程的创建
pthread_create函数
功能:
函数原型:
参数
错误码的解析:
创建多个线程
代码示例:
#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;
}
线程终止
如何终止
退出方法
线程的等待
概念
那我们可以不关心退出信息吗?
pthread_join函数
原型
int pthread_join(pthread_t thread,void **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_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;
}
结论:
分离线程
线程是可以等待的、等待的时候,阻塞式等待。
如果我们不想等待呢?
分离线程
使用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;
}
代码结论:
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;
}
原生线程库
概念
原生线程库中,可能要存在多个线程--你用这些接口创建了线程,别人可以同时再用吗?
要不要对线程进行管理呢?
线程的属性:
Linux的方案:
线程id是该线程在线程库中该线程TCB的地址。独立栈在共享区的库中。
栈的分布。
线程的局部存储:
封装一个原生线程库
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;
}