一、線程庫管理
tid其實是一個地址?
void* start(void* args)
{const char* name = (const char *)args;while(true){printf("我是新線程 %s ,我的地址:0x%lx\n",name,pthread_self());sleep(1);}return nullptr;
}int main()
{pthread_t tid1;pthread_create(&tid1,nullptr,start,(void*)"thread-1");printf("我是主線程,我的地址:0x%lx\n",pthread_self());void* ret = nullptr;pthread_join(tid1,&ret);return 0;
}
zxw@hcss-ecs-cc58:~/linux_-ubuntu/class/lesson7$ ./mythread
我是主線程,我的地址:0x7f9dbdc333c0
我是新線程 thread-1 ,我的地址:0x7f9dbdc2f640
我是新線程 thread-1 ,我的地址:0x7f9dbdc2f640
我是新線程 thread-1 ,我的地址:0x7f9dbdc2f640
首先,我們知道pthread. h不是操作系統的接口,而是原生線程庫。那么用戶創建的線程,操作系統無法管理,則需要線程庫來進行管理。他從系統中獲取輕量級進程相關屬性,從用戶中也獲取一些屬性,這樣就先描述起來了,再通過數據結構將線程組織起來,就將線程管理好了。
那么線程庫如何管理呢,在哪管理呢???
在進程地址空間中,通過?
mmap
?(共享區)加載了動態庫,我們使用的?pthread
?庫就在該區域。pthread
?庫會管理進程中的每一個線程,它使用一系列的數據結構(如線程控制塊)來組織和維護線程的相關信息。每個線程還擁有獨立的棧空間,主線程的棧由操作系統在進程啟動時自動創建,位于進程地址空間的默認棧區;而通過?
pthread_create
?創建的其他線程,其棧空間默認由操作系統在進程地址空間的線程私有區域進行分配。當調用?
pthread
?相關函數時,實際上是對?pthread
?庫所管理的這些數據結構進行訪問和操作,從而實現對線程的創建、同步、銷毀等管理功能。
那么現在,我們也可以理解 pthread_t tid 是什么了,他不就是每一個線程在進程地址空間的起始地址嘛,我們pthread_create 對tid進行寫入,因為需要創建對應的數據結構,找到起始地址,然后返回,后續用戶要繼續對線程進行控制,等待啊,終止啊,分離啊,取消啊。都需要傳入tid,也就是能找到在進程地址空間的位置后,才可以處理。
二、線程的局部存儲?
int g_val = 100;void *TreadFun(void *arg)
{while (1){cout << "我是一個新線程,g_val: " << g_val << ",&g_val: " << &g_val << endl;g_val++;sleep(1);}return nullptr;
}int main()
{pthread_t tid;pthread_create(&tid, NULL, TreadFun, (void *)"Thread 1");while (1){cout << "我是一個主線程,g_val: " << g_val << ",&g_val: " << &g_val << endl;sleep(1);}pthread_join(tid,nullptr);
}
zxw@hcss-ecs-cc58:~/linux_-ubuntu/class/lesson7/Pthread$ ./testThread
我是一個主線程,g_val: 100,&g_val: 0x55a1a3616014
我是一個新線程,g_val: 100,&g_val: 0x55a1a3616014
我是一個主線程,g_val: 101,&g_val: 0x55a1a3616014
我是一個新線程,g_val: 101,&g_val: 0x55a1a3616014
我是一個主線程,g_val: 102,&g_val: 0x55a1a3616014
我是一個新線程,g_val: 102,&g_val: 0x55a1a3616014
我是一個主線程,g_val: 103,&g_val: 0x55a1a3616014
我是一個新線程,g_val: 103,&g_val: 0x55a1a3616014
如果我們給全局變量前添加上__thread,GCC/G++編譯器提供的一個擴展,用于聲明線程局部存儲變量。?
__thread int g_val = 100;
重新執行程序,發現地址發生改變
我是一個主線程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一個新線程,g_val: 102,&g_val: 0x7f8c2a39763c
我是一個主線程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一個新線程,g_val: 103,&g_val: 0x7f8c2a39763c
我是一個主線程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一個新線程,g_val: 104,&g_val: 0x7f8c2a39763c
因為我們添加的__thread 會在G++編譯時,給每個線程的局部存儲空間里將變量拷貝進程,私有一份,于是每個線程自己管理自己的那一份資源。?
__thread只能修飾內置類型,如string這種數據結構和自定義類型無法處理。
?三、線程封裝?
#ifndef _THREAD_HPP__
#define _THREAD_HPP__
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdio>
using namespace std;namespace MyThread
{template <typename T>using func_t = function<void(T)>;static int number = 1;enum TSTATUS{NEW,RUNNING,STOP};template <typename T>class Thread{private:// 成員方法! void* Routinue(Thread* this,void* args)static void *Routinue(void *args){Thread<T> *t = (Thread<T> *)args;t->_status = TSTATUS::RUNNING;t->_func(t->_data);return nullptr;}void EnableDetach(){_joinable = false;}public:Thread(func_t<T> func, T data) : _func(func), _status(TSTATUS::NEW), _joinable(true), _data(data){_name = "Thread-" + to_string(number++);_pid = getpid();}bool Start(){if (_status != TSTATUS::RUNNING){int n = ::pthread_create(&_tid, nullptr, Routinue, this);if (n != 0)return false;return true;}return false;}bool Stop(){if (_status == TSTATUS::RUNNING){int n = ::pthread_cancel(_tid);if (n != 0)return false;return true;}return false;}bool Join(){if (_joinable){int n = ::pthread_join(_tid, nullptr);if (n != 0)return false;_status = TSTATUS::STOP;return true;}return false;}void Detach(){EnableDetach();pthread_detach(_tid);}bool IsJoinable() { return _joinable; }string Name() { return _name; }~Thread(){}private:string _name;pthread_t _tid;pid_t _pid;bool _joinable; // 是否分離,默認不是func_t<T> _func;TSTATUS _status;T _data;};}#endif