1.启动线程(创建线程)
- 普通正常启动(构造临时对象)
自己的Demo,参考代码如下:
#include <thread>
#include <iostream>
void do_something();
void do_something_else();
void do_something(){
std::cout<<"cherno love c++"<<std::endl;
}
void do_something_else(){
std::cout<<"welcome to my program world"<<std::endl;
}
class my_first_thread{
public:
void operator()()const{
do_something_else();
}
public:
my_first_thread() noexcept=default;
// my_first_thread(const my_first_thread&other)=delete;
my_first_thread &operator=(const my_first_thread &other)=delete;
~my_first_thread()=default;
};
int main(int argc,char **argv){
do_something();
my_first_thread m;
std::thread t(m);
t.join();
return 0;
}
运行结果:
cherno love c++
welcome to my program world
上述代码通过构造类的临时对象,将该临时对象作为线程构造函数参数,完成线程的创建。
- 非正常启动(启动失败)
启动失败参考代码如下:
#include <thread>
#include <iostream>
void do_something();
void do_something_else();
void do_something(){
std::cout<<"cherno love c++"<<std::endl;
}
void do_something_else(){
std::cout<<"welcome to my program world"<<std::endl;
}
class my_first_thread{
public:
void operator()()const{
do_something_else();
}
public:
my_first_thread() noexcept=default;
// my_first_thread(const my_first_thread&other)=delete;
my_first_thread &operator=(const my_first_thread &other)=delete;
~my_first_thread()=default;
};
int main(int argc,char **argv){
do_something();
// my_first_thread m;
// std::thread t(m);
std::thread t(my_first_thread());
t.join();
return 0;
}
上述代码是将运算符重载函数作为线程构造函数参数,导致线程创建失败。
- 其他方式启动线程(Lambda表达式)
对上述代码修改,参考代码如下:
#include <thread>
#include <iostream>
void do_something();
void do_something_else();
void do_something(){
std::cout<<"cherno love c++"<<std::endl;
}
void do_something_else(){
std::cout<<"welcome to my program world"<<std::endl;
}
class my_first_thread{
public:
void operator()()const{
do_something_else();
}
public:
my_first_thread() noexcept=default;
// my_first_thread(const my_first_thread&other)=delete;
my_first_thread &operator=(const my_first_thread &other)=delete;
~my_first_thread()=default;
};
int main(int argc,char **argv){
do_something();
std::thread t([](){
do_something_else();
});
t.join();
return 0;
}
运行结果:
cherno love c++
welcome to my program world
2.detach
示例代码参考如下:
#include <thread>
#include <iostream>
class danger_thread{
private:
unsigned int value;
public:
danger_thread() noexcept=default;
danger_thread(const unsigned int value):value(value){};
// danger_thread(const danger_thread &other)=delete;
danger_thread &operator=(const danger_thread &other)=delete;
~danger_thread()=default;
public:
void operator()(){
for(unsigned int i=0;i<value;i++){
std::cout<<i<<"."<<"chrno love threads program"<<std::endl;
}
}
};
void thread_start(){
int local_value=10;
std::thread t{danger_thread(local_value)};
t.detach();
}
int main(int argc,char **argv){
thread_start();
return 0;
}
运行结果:
detach的作用:不阻塞线程。
具体区别图(join与detach函数):
- join:
主线程等待新线程完成后,才继续运行。
- detach:
主线程并未等待线程完成,而是自顾自地运行。
3.异常处理线程等待
#include <iostream>
#include <thread>
#include <exception>
void do_something_else(){
std::cout<<"cherno"<<std::endl;
}
struct danger_thread{
unsigned int &value;
danger_thread(unsigned int value):value(value){}
void operator()(){
for(unsigned int i=0;i<value;i++){
std::cout<<i<<":"<<"I love c++ Porgram"<<std::endl;
}
}
};
void thread_start(){
unsigned int local_value=10;
danger_thread my_thread(local_value);
std::thread t(my_thread);
try
{
do_something_else();
}
catch(...)
{
t.join();
throw;
}
t.join();
}
int main(int argc,char *argv[]){
thread_start();
return 0;
}
在捕捉到异常时添加join,并在外层添加join,保证了异常处理时,正确的处理方式。
- 使用RAII机制重构线程构造
代码如下:
#include <iostream>
#include <thread>
void do_something_else(){
std::cout<<"cherno"<<std::endl;
}
class thread_guard{
private:
std::thread &t;
public:
explicit thread_guard(std::thread &t_):t(t_){}
~thread_guard(){
if(t.joinable())
t.join();
}
thread_guard(const thread_guard &other)=delete;
thread_guard &operator=(const thread_guard &other)=delete;
};
struct danger_thread{
unsigned int value;
danger_thread(unsigned int value):value(value){}
void operator()(){
for(unsigned int i=0;i<value;i++){
std::cout<<i<<":"<<"I love c++ Program"<<std::endl;
}
}
};
void thread_start(){
unsigned int local_value=10;
danger_thread my_thread(local_value);
std::thread t(my_thread);
thread_guard guard(t);
do_something_else();
}
int main(int argc,char **argv){
thread_start();
return 0;
}
思想:主要在析构时进行join
RAII机制
RAII即资源分配即初始化。
RAII原理:
资源的使用通常是分为三个步骤:
- 获取资源
- 使用资源
- 释放资源
简单来说,RAII主要是为释放资源做工作的,即无需人工释放,而是靠操作系统自动回收。
RAII的应用:
RAII主要应用于互斥锁,保护共享资源。
4.线程构造函数传递参数
- 普通函数传参
示例代码:
void func(int i,const std::string &s);
const int maxn=1024;
void thread_start(){
char buffer[maxn];
std::thread t(func,5,"hello");
}
注意:在传递参数时一定要传入与形参一样形式的参数。
总结:普通函数传参顺序依次是函数、参数。
- 类函数传参(无参数)
class Person{
public:
void do_something();
};
Person person;
std::thread t(&Person::do_something,&person);
- 类函数传参(有参数):
class Person{
public:
void do_something(int i);
};
Person person;
int num(10);
std::thread t(&Person::do_something,&person,num);
总结类函数传参顺序依次是类函数地址、临时类实例地址、其他参数。
移动线程:
unique_ptr支持移动构造
示例代码:
class big_object;
void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object>p(new big_object);
p->prepare_data(12);
std::thread__ptr t(process_big_object,std::move(p));
流程如图所示:
线程与unique一样支持移动。
5.Scoped_thread实现
Scoped_thread类实现,代码如下:
#include <thread>
#include <iostream>
#include <exception>
#include <excpt.h>
void do_something(int value){
for(int i=0;i<value;i++){
std::cout<<i<<":"<<"I love c++ Program"<<std::endl;
}
}
void do_something_else(){
std::cout<<"cherno"<<std::endl;
}
class scoped_thread{
private:
std::thread t;
public:
scoped_thread(std::thread &t_):t(std::move(t_)){
if(!t.joinable())
throw std::logic_error("no thread");
}
~scoped_thread(){
t.join();
}
scoped_thread(const scoped_thread &other)=delete;
scoped_thread&operator=(const scoped_thread &other)=delete;
};
struct func{
unsigned int value;
func(unsigned int value):value(value){}
void operator()(){
do_something(value);
}
};
void thread_start(){
unsigned int local_value=10;
std::thread t([local_value](){
do_something(local_value);
});
scoped_thread my_thread(std::ref(t));
do_something_else();
}
int main(int argc,char *argv[]){
thread_start();
return 0;
}
运行结果:
cherno
0:I love c++ Program
1:I love c++ Program
2:I love c++ Program
3:I love c++ Program
4:I love c++ Program
5:I love c++ Program
6:I love c++ Program
7:I love c++ Program
8:I love c++ Program
9:I love c++ Program
总结:Scoped_thread采用RAII机制,支持线程移动构造。
补充:智能指针的头文件是<memory>
完整的Scoped_thread实现如下:
#include <thread>
class joining_thread{
private:
std::thread t;
public:
joining_thread() noexcept=default;
template<typename Callable,typename ...Args>//变参模版
//变参模版与std::forward移动函数
explicit joining_thread(Callable &&func,Args&&...args):t(std::forward<Callable>(func),std::forward<Args>(args)...){}
explicit joining_thread(std::thread t_)noexcept:t(std::move(t_)){}
joining_thread(joining_thread &other)noexcept:t(std::move(other.t)){}
joining_thread &operator=(std::thread &t_)noexcept{
if(joinable())
join();
t=std::move(t_);
return *this;
}
joining_thread &operator=(joining_thread &other)noexcept{
if(joinable())
join();
t=std::move(other.t);
return *this;
}
~joining_thread(){
if(t.joinable())
t.join();
}
void swap(joining_thread &other)noexcept{
t.swap(other.t);
}
std::thread::id get_id() const noexcept{
return t.get_id();
}
bool joinable()const noexcept{
return t.joinable();
}
void join(){
t.join();
}
void detach(){
t.detach();
}
std::thread &as_thread()noexcept{
return t;
}
const std::thread &as_thread()const noexcept{
return t;
}
};
量产线程,等待完成
代码如下:
void do_work(unsigned int id);
void f(){
std::vector<std::thread>threads;
for(unsigned int i=0;i<20;i++){
threads.push_back(std::thread(do_work,i));
}
std::for_each(threads.begin(),threads.end(),std::mem_fn(&std::thread::join));
}
6.实战(并行版本的求和)(自己实现)
代码如下:
#include <iostream>
#include <thread>
#include <vector>
#include <functional>
#define NUM_THREAD 10
template <typename Iterator, typename T>
T & my_sum(Iterator begin, Iterator end, T &init){
unsigned const long length = std::distance(begin, end);
if(length == 0)
throw std::logic_error("can not calculate sum");
for(auto iter = begin; iter!=end; iter++){
init += *iter;
}
return init;
}
template <typename Iterator, typename T>
struct sum_thread{
void operator()(Iterator begin, Iterator end, T &result){
result = my_sum(begin, end, result);
}
};
template <typename Iterator, typename T>
T parallel_sum(Iterator begin, Iterator end, T init){
unsigned const int thread_numbers = NUM_THREAD;
unsigned const long length = std::distance(begin, end);
if(length == 0)
return init;
const int block_size = length/thread_numbers;
std::vector<T> results(thread_numbers);
std::vector<std::thread> threads(thread_numbers);
Iterator block_start = begin;
Iterator block_end = begin;
for(unsigned int i = 0; i < thread_numbers-1; i++){
std::advance(block_start, block_size);
threads[i] = std::thread(sum_thread<Iterator, T>(),
block_start,
block_end,
results[i]);
block_start = block_end;
}
threads[thread_numbers-1] = std::thread(sum_thread<Iterator, T>(),
block_start,
end,
results[thread_numbers-1]);
std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
// std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
return my_sum(results.begin(), results.end(), 0);
}