@TOC
📝線程封裝
🌉 Thread.hpp
// Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
namespace ThreadModule
{
// 原⼦計數器,⽅便形成線程名稱
std::uint32_t cnt = 0;
// 線程要執⾏的外部⽅法,我們不考慮傳參,後續有std::bind 來進⾏類間耦合
using threadfunc_t = std::function<void()>;
// 線程狀態
enum class TSTATUS
{
THREAD_NEW,
THREAD_RUNNING,
THREAD_STOP
};
// 線程
class Thread
{
private:
static void *run(void *obj)
{
Thread *self = static_cast<Thread *>(obj);
pthread_setname_np(pthread_self(), self->_name.c_str()); // 設置線程名稱
self->_status = TSTATUS::THREAD_RUNNING;
if (!self->_joined)
{
pthread_detach(pthread_self());
}
self->_func();
return nullptr;
}
void SetName()
{
// 後期加鎖保護
_name = "Thread-" + std::to_string(cnt++);
}
public:
Thread(threadfunc_t func) : _status(TSTATUS::THREAD_NEW),
_joined(true), _func(func)
{
SetName();
}
void EnableDetach()
{
if (_status == TSTATUS::THREAD_NEW)
_joined = false;
}
void EnableJoined()
{
if (_status == TSTATUS::THREAD_NEW)
_joined = true;
}
bool Start()
{
if (_status == TSTATUS::THREAD_RUNNING)
return true;
int n = ::pthread_create(&_id, nullptr, run, this);
if (n != 0)
return false;
return true;
}
bool Join()
{
if (_joined)
{
int n = pthread_join(_id, nullptr);
if (n != 0)
return false;
return true;
}
return false;
}
~Thread() {}
private:
std::string _name;
pthread_t _id;
TSTATUS _status;
bool _joined;
threadfunc_t _func;
};
}
🌉 Makefile
// main.cc
#include <iostream>
#include <unistd.h>
#include "Thread.hpp"
void hello1()
{
char buffer[64];
pthread_getname_np(pthread_self(), buffer, sizeof(buffer) - 1);
while (true)
{
}
std::cout << "hello world, " << buffer << std::endl;
sleep(1);
}
void hello2()
{
char buffer[64];
pthread_getname_np(pthread_self(), buffer, sizeof(buffer) - 1);
while (true)
{
std::cout << "hello world, " << buffer << std::endl;
sleep(1);
}
}
int main()
{
pthread_setname_np(pthread_self(), "main");
ThreadModule::Thread t1(hello1);
t1.Start();
ThreadModule::Thread t2(std::bind(&hello2));
t2.Start();
t1.Join();
t2.Join();
return 0;
}
運⾏結果查詢
$ ps -aL
PID LWP TTY TIME CMD
195828 195828 pts/1 00:00:00 main
195828 195829 pts/1 00:00:00 Thread-0
195828 195830 pts/1 00:00:00 Thread-1
🌠線程封裝第一版
🌉 Makefile:
bin=testThread
cc=g++
src=$(wildcard *.cc)
obj=$(src:.cc=.o)
$(bin):$(obj)
$(cc) -o $@ $^ -lpthread
%.o:%.cc
$(cc) -c $< -std=c++17
.PHONY:test
test:
echo $(src)
echo $(obj)
🌉Main.cc
#include "Thread.hpp"
#include <unordered_map>
#include <memory>
// using thread_ptr_t = std::shared_ptr<ThreadModule::Thread>;
#define NUM 10;
class threadData
{
public:
int max;
int start;
};
void Count(threadData td)
{
for(int i = td.start; i < td.max; i++)
{
std::cout<< "i == " <<i <<std::endl;
sleep(1);
}
}
int main()
{
threadData td;
td.max = 60;
td.start = 50;
//使用lamda表達式封裝Count成一個不接受參數的可調用對象
auto func = [td]()
{
Count(td);
};
ThreadModule::Thread<threadData> t(func);
t.Start();
t.Join();
return 0;
}
🌉 Thread.hpp:
//V1
namespace ThreadModule
{
// template<typename T>
using func_t = std::function<void()>;
static int number = 1;
enum class TSTATUS
{
NEW,
RUNNING,
STOP
};
template<typename T>
class Thread
{
private:
static void* Routine(void *args)
{
Thread* t = static_cast<Thread *>(args);
t->_status = TSTATUS::RUNNING;
t->_func();
return nullptr;
}
void EnableDetach()
{
_joinable = false;
}
public:
Thread(func_t func)
: _func(func), _status(TSTATUS::NEW), _joinable(true)
{
_name = "Thread - " + std::to_string(number++);
_pid = getpid();
}
bool Start()
{
if (_status != TSTATUS::RUNNING)
{
int n = ::pthread_create(&_tid, nullptr, Routine, this); // 這裏使用this
if (n != 0)
return false;
return true;
}
return true;
}
bool Stop()
{
if (_status != TSTATUS::RUNNING)
{
int n = ::pthread_cancel(_tid);
if (n != 0)
return false;
_status = TSTATUS::STOP;
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;
}
std::string Name()
{
return _name;
}
~Thread()
{
}
private:
std::string _name;
pthread_t _tid;
pid_t _pid;
bool _joinable; // 是否是分離的, 默認不是
func_t _func;
TSTATUS _status;
};
}
#endif
🌠線程封裝第二版
🌉 Thread.hpp:
#ifndef THREAD_HPP
#define THREAD_HPP
#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>
//V2
namespace ThreadModule
{
static int number = 1;
enum class TSTATUS
{
NEW,
RUNNING,
STOP
};
template<typename T>
class Thread
{
using func_t = std::function<void(T)>;
private:
//成員方法
static void *Routine(void* args)
{
Thread<T> *t = static_cast<Thread<T>*>(args);
t->_status = TSTATUS::RUNNING;
t->_func(t->_data);
return nullptr;
}
void EnableDetach()
{
_joinable = false;
}
public:
Thread(func_t func, T data)
:_func(func)
,_data(data)
,_status(TSTATUS::NEW)
,_joinable(true)
{
_name = "Thread -" +std::to_string(number++);
_pid = getpid();
}
bool Start()
{
if(_status != TSTATUS::RUNNING)
{
int n = pthread_create(&_tid, nullptr, Routine, 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;
_status = TSTATUS::STOP;
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;
}
bool IsJoinale()
{
return _joinable;
}
std::string Name()
{
return _name;
}
~Thread()
{
}
private:
std::string _name;
pthread_t _tid;
bool _joinable;//是否是分離的,默認不是
func_t _func;
pid_t _pid;
TSTATUS _status;
T _data;
};
}
#endif
🌉 Main.cc
#include "Thread.hpp"
#include <unordered_map>
#include <memory>
using thread_ptr_t = std::shared_ptr<ThreadModule::Thread<int>>;
#define NUM 10
class threadData
{
public:
int max;
int start;
};
void Count(threadData td)
{
for(int i = td.start; i < td.max; i++)
{
std::cout<< "i == " <<i <<std::endl;
sleep(1);
}
}
int main()
{
//先描述再組織
std::unordered_map<std::string, thread_ptr_t> threads;
//如果我們要創建多線程呢?
for(int i = 0; i< NUM ; i++)
{
auto func = []()
{
while(true)
{
std::cout<< "hello world" << std::endl;
sleep(1);
}
};
int threadData = i+1;
thread_ptr_t t= std::make_shared<ThreadModule::Thread<int>>(
[func](int)
{
func();
},
threadData
);
std::cout<< "Create thread with name : "<<t->Name() <<std::endl;
threads[t->Name()] = t;
}
for(auto &thread : threads)
{
thread.second->Start();
}
for(auto &thread : threads)
{
thread.second->Join();
}
🌠單線程創建測試
🌉 Thread.hpp
#ifndef THREAD_HPP
#define THREAD_HPP
#include <iostream>
#include <string>
#include <pthread.h>
#include <functional>
#include <sys/types.h>
#include <unistd.h>
//V2
namespace ThreadModule
{
static int number = 1;
enum class TSTATUS
{
NEW,
RUNNING,
STOP
};
template<typename T>
class Thread
{
using func_t = std::function<void(T)>;
private:
//成員方法
static void *Routine(void* args)
{
Thread<T> *t = static_cast<Thread<T>*>(args);
t->_status = TSTATUS::RUNNING;
t->_func(t->_data);
return nullptr;
}
void EnableDetach()
{
_joinable = false;
}
public:
Thread(func_t func, T data)
:_func(func)
,_data(data)
,_status(TSTATUS::NEW)
,_joinable(true)
{
_name = "Thread -" +std::to_string(number++);
_pid = getpid();
}
bool Start()
{
if(_status != TSTATUS::RUNNING)
{
int n = pthread_create(&_tid, nullptr, Routine, 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;
_status = TSTATUS::STOP;
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;
}
bool IsJoinale()
{
return _joinable;
}
std::string Name()
{
return _name;
}
~Thread()
{
}
private:
std::string _name;
pthread_t _tid;
bool _joinable;//是否是分離的,默認不是
func_t _func;
pid_t _pid;
TSTATUS _status;
T _data;
};
}
🌉 main.cc
#include "Thread.hpp"
#include <unordered_map>
#include <memory>
using thread_ptr_t = std::shared_ptr<ThreadModule::Thread<int>>;
#define NUM 10
class threadData
{
public:
int max;
int start;
};
void Count(threadData td)
{
for(int i = td.start; i < td.max; i++)
{
std::cout<< "i == " <<i <<std::endl;
sleep(1);
}
}
int main()
{
auto func = []()
{
while(true)
{
std::cout<< "hello world" <<std::endl;
sleep(1);
}
};
std::function<void(int)> wrappedFunc2 = [func](int){ func(); };
int signalTreadData = 5;
ThreadModule::Thread<int> t( wrappedFunc2,
signalTreadData);
// std::cout<< "Create thread with name : "<<t->Name() <<std::endl;
// t.Start();
// t.Join();
t.Start();
std::cout<< t.Name() << " is running" <<std::endl;
std::cout<<std::flush;//手動刷新緩衝期
sleep(5);
if (t.Stop()) {
std::cout << "Stop thread : " << t.Name() << std::endl;
std::cout << std::flush; // 手動刷新緩衝區
} else {
std::cout << "Failed to stop thread : " << t.Name() << std::endl;
std::cout << std::flush; // 手動刷新緩衝區
}
sleep(1);
if (t.Join()) {
std::cout << "Join thread : " << t.Name() << std::endl;
std::cout << std::flush; // 手動刷新緩衝區
} else {
std::cout << "Failed to join thread : " << t.Name() << std::endl;
std::cout << std::flush; // 手動刷新緩衝區
}
return 0;
}
🌠智能指針std::shared_ptr
std::shared_ptr 是 C++ 標準庫 <memory> 頭文件中提供的一種智能指針,用於管理動態分配的對象,它實現了共享所有權的語義,下面為你詳細介紹它的作用、工作原理以及在你給出的代碼中的使用場景。
作用
在傳統的 C++ 中,使用 new 操作符動態分配內存後,需要手動使用 delete 操作符釋放內存,否則會導致內存泄漏。std::shared_ptr 可以自動管理動態分配的對象的生命週期,當沒有任何 std::shared_ptr 指向該對象時,它會自動釋放對象所佔用的內存,從而避免了手動管理內存帶來的複雜性和潛在的內存泄漏問題。
工作原理
std::shared_ptr 使用引用計數的機制來管理對象的生命週期。每個 std::shared_ptr 都維護一個引用計數,記錄有多少個 std::shared_ptr 共享同一個對象。當一個新的 std::shared_ptr 指向一個對象時,引用計數加 1;當一個 std::shared_ptr 被銷燬或者指向其他對象時,引用計數減 1。當引用計數變為 0 時,説明沒有任何 std::shared_ptr 再指向該對象,此時 std::shared_ptr 會自動調用對象的析構函數並釋放內存。
using thread_ptr_t = std::shared_ptr<ThreadModule::Thread<int>>;這行代碼使用using關鍵字定義了一個類型別名thread_ptr_t,它實際上是std::shared_ptr<ThreadModule::Thread<int>>的別名。這樣做的好處是可以簡化代碼,避免在後續使用時多次書寫冗長的類型名。這裏的ThreadModule::Thread<int>是一個模板類的實例化,表示一個線程對象,std::shared_ptr用於管理這個線程對象的生命週期。std::unordered_map<std::string, thread_ptr_t> threads;這行代碼定義了一個std::unordered_map,它是一個無序關聯容器,用於存儲鍵值對。鍵的類型是std::string,值的類型是thread_ptr_t,也就是std::shared_ptr<ThreadModule::Thread<int>>。通過這種方式,可以將線程對象與一個字符串鍵關聯起來,方便對線程對象進行管理和查找。thread_ptr_t t = std::make_shared<ThreadModule::Thread<int>>( ... );這行代碼使用std::make_shared函數創建了一個std::shared_ptr<ThreadModule::Thread<int>>對象,並將其賦值給t。std::make_shared是一個便捷的函數,用於創建std::shared_ptr對象,它會在一次內存分配中同時分配對象和引用計數所需的內存,比分別使用new和std::shared_ptr的構造函數更加高效。括號內的參數是傳遞給ThreadModule::Thread<int>構造函數的參數,用於初始化線程對象。
示例代碼
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
void doSomething() { std::cout << "Doing something..." << std::endl; }
};
int main() {
// 創建一個 std::shared_ptr 對象
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
// 複製一個 std::shared_ptr 對象,引用計數加 1
std::shared_ptr<MyClass> ptr2 = ptr1;
// 調用對象的成員函數
ptr1->doSomething();
// 當 ptr1 和 ptr2 離開作用域時,引用計數減 1
// 當引用計數變為 0 時,對象會被自動銷燬
return 0;
}
代碼解釋
- 在
main函數中,首先使用std::make_shared創建了一個std::shared_ptr<MyClass>對象ptr1,此時引用計數為 1。 - 然後將
ptr1賦值給ptr2,引用計數變為 2。 - 調用
ptr1->doSomething()來調用對象的成員函數。 - 當
ptr1和ptr2離開main函數的作用域時,它們會被銷燬,引用計數減 1。當引用計數變為 0 時,MyClass對象的析構函數會被自動調用,釋放對象所佔用的內存。
🚩總結
如果要像C++11那樣進⾏可變參數的傳遞,是可以這樣設計的,但是太⿇煩了,真到了哪⼀步,就直接⽤c++11吧,我們的⽬標主要是理解系統概念對象化,此處不做複雜設計,⽽且後續可以使⽤std::bind來進⾏對象間調⽤