一: 向线程函数传递参数
thread的构造函数里第一个参数为调用对象, 从第二个参数起, 为线程函数的第一个参数
//若用cout<< *** <<endl输出可能会乱序
void func(int i,string s)
{
printf("i=%d,s=%s",i,s.c_str());
}
int main()
{
int i=1;
char s[]="test";
//第一个参数为调用对象, 从第二个参数起, 为线程函数的第一个参数
thread t1(func,i,s);
t1.detach();
}
----------------------------
线程内部有存储空间, 参数会按照默认方式复制到该处, 新创建的线程才能直接访问他们
上面的代码数组s以char const*的形式传入, 进入线程的上下文环境以后, 才转换为string类型
但有问题:
可能没转换之前, 主线程已经退出,并销毁s, 造成子线程对s的悬空引用
一个解决办法如下: 加上string(s)保证在传进参数之前已经转化为string对象
//若用cout<< *** <<endl输出可能会乱序
void func(int i,const string &s)
{
printf("i=%d,s=%s",i,s.c_str());
}
int main()
{
int i=1;
char s[]="test";
//第一个参数为调用对象, 从第二个参数起, 为线程函数的第一个参数
//加上string(s)保证在传进参数之前已经转化为string对象
thread t1(func,i,string(s));
t1.detach();
}
-----------------------
考虑另一种情形, 参数需要非const引用, 而不复制对象, 以下代码编译失败
void func(int &i)
{
printf("i=%d");
}
int main()
{
int i=1;
//编译失败
thread t1(func,i);
t1.join();
}
原因: thread构造函数对func不知情, 会把参数直接复制, 之后以右值的形式传递, 最后func函数会收 到一个右值作为参数, 编译失败
解决: 调用ref函数包装
void func(int &i)
{
printf("i=%d");
}
int main()
{
int i=1;
//编译通过
thread t1(func,ref(i));
t1.join();
}
-----------
另一种传递参数的方法: 通过unique传递动态参数
如下
class A
{
public:
int i;
A(int i) : i(i)
{ printf("constructor\n"); }
void func()
{
cout << "in A::func()" << endl;
}
};
void func(unique_ptr<A> p)
{
printf("i=%d", p->i);
}
int main()
{
unique_ptr<A> p(new A(1));
//通过move把对象转移给线程,此时不额外创建新对象
thread t1(func, move(p));
t1.join();
}
二: 移交线程归属权
书中的例子
void func1()
{
printf("in func1");
}
void func2()
{
printf("in func2");
}
int main()
{
thread t1(func1);
thread t2 = move(t1); //线程t1归属权转为t2
cout<< t1.joinable(); // 输出0
t1 = thread(func2); //启动另一新线程, 随即归属权转移为t1
thread t3;
t3 = move(t2); //归属权t2->t3
//出错, 此时t1已关联运行func2()函数的线程, terminate()会被调用, 程序终结
t1 = move(t3);
//省略调用join()
}
可以得到:
1可以通过move()函数将线程归属权移出
2可以通过=号获取新创建的线程归属权
3当对象已经关联有线程时, 不可用关联另一线程(通过move()和通过=号都不可以)
thread支持移动操作, 函数可以向外传递线程, 也可以向函数传递线程
//接收thread参数
void func2(thread t)
{
printf("in func2");
t.join();
}
//从内部返回thread对象
thread get_thread()
{
thread t(func1);
return t;
}
int main()
{
thread t=get_thread();//获取thread对象
t.join();
func2(thread(func1));//传递thread参数
}
------------------
在上一篇里介绍了thread_guard类, 即对象销毁时自动调用内部线程对象join()函数, 再加上支持归属权移动功能, 实现一个可行的joining_thread类(仿造c++20的jthread类)
class joining_thread
{
thread t;
public:
joining_thread() noexcept = default;
template<class Callable, class ...Args>
explicit joining_thread(Callable &&func, Args &&...args1):
t(forward<Callable>(func), forward<Args>(args1)...)
{}
explicit joining_thread(thread t_) noexcept: t(move(t_))
{}
joining_thread(joining_thread &&other) noexcept:
t(move(other.t))
{}
joining_thread &operator=(joining_thread &&other) noexcept
{
if (joinable())
{
join();
}
t = move(other.t);
return *this;
}
joining_thread &operator=(thread &&other) noexcept
{
if (joinable())
{
join();
}
t = move(other);
return *this;
}
~joining_thread() noexcept
{
if (joinable())
{
join();
}
}
void swap(joining_thread &other) noexcept
{t.swap(other.t);}
thread::id get_id() const noexcept
{return t.get_id();}
bool joinable() const noexcept
{return t.joinable();}
void join() noexcept
{t.join();}
void detach() noexcept
{t.detach();}
const thread &as_thread() const noexcept
{return t;}
};
三: 并行版的accumulate完整实现
算是第一个多线程编程实战
#include "iostream"
#include "thread"
#include "vector"
#include "numeric" //accumulate在此头文件
using namespace std;
template<typename Iterator,typename T>
struct accumulate_block
{
void operator()(Iterator first,Iterator last,T&result)
{
result= accumulate(first,last,result);
}
};
template<typename Iterator,typename T>
T parallel_accumulate(Iterator first,Iterator last,T init)
{
int length= distance(first,last);
if(length==0)return init;
//确定需要多少线程合适, hardware_concurrency为可真正并发的线程数量
int threads_num=min(length/20-1,(int)thread::hardware_concurrency());
//若length太小, 则用2个子线程
threads_num=max(2,threads_num);
int block_size=length/threads_num;//确定每个线程处理的block_size
vector<T> results(threads_num); //存储各个线程暂存的结果(包括主线程)
vector<thread> threads(threads_num-1);//主线程算一个, 因此少创建1个子线程
Iterator block_first=first;
int i=0;
for(i=0 ;i <(threads_num-1); i++)
{
Iterator block_end=block_first;
advance(block_end,block_size); //advance函数将block_end向前移block_size个大小
threads[i]=thread(accumulate_block<Iterator,T>(),block_first,block_end,ref(results[i]));
block_first=block_end;
}
accumulate_block<Iterator,T>()(block_first,last,results[threads_num-1]);//最后一段block由主线程处理
for(auto &entry:threads)
{
entry.join();
}
return accumulate(results.begin(),results.end(),init);
}
int main()
{
vector a={1,2,3,4,5,6,7,8,9,10};
cout<< parallel_accumulate(a.begin(),a.end(),0) <<endl;//输出55
}