cpp多线程编程demo1
- 原作者 https://blog.csdn.net/lijinqi1987/category_5784833.html
创建线程
c++11线程库
原始的c++标准仅支持单线程编程,新的c++标准(c++11或c++0x)于2011年发布,引入了新的线程库。
编译器要求
- Linux: gcc 4.8.1 (完全并发支持)
- Windows: Visual Studio 2012 and MingW
在linux下的编译方法:
g++ -std=c++11 sample.cpp -lpthread
在c++11中创建线程
在每个c++应用程序中,都有一个默认的主线程,即main函数,在c++11中,我们可以通过创建std::thread
类的对象来创建其他线程,每个std::thread
对象都可以与一个线程相关联。
需包含头文件
std::thread
对象将执行什么?
我们可以使用std::thread
对象附加一个回调,当这个新线程启动时,它将被执行。 这些回调可以是,
- 函数指针
- 函数对象
- Lambda函数
线程对象可以这样创建
std::thread thObj(<CALLBACK>)
新线程将在创建新对象后立即开始,并且将与已启动的线程并行执行传递的回调。此外,任何线程可以通过在该线程的对象上调用join()函数来等待另一个线程退出。
看一个主线程创建单独线程的例子,创建完新的线程后,主线程将打印一些信息并等待新创建的线程退出。
使用函数指针创建线程:
#include <iostream>
#include <thread>
void thread_function() {
for (int i = 0; i < 100; i++)
std::cout << "thread function excuting" << std::endl;
}
int main() {
std::thread threadObj(thread_function);
for (int i = 0; i < 100; i++)
std::cout << "Display from MainThread" << std::endl;
threadObj.join();
std::cout << "Exit of Main function" << std::endl;
return 0;
}
使用函数对象创建线程:
#include <iostream>
#include <thread>
class DisplayThread {
public:
void operator ()() {
for (int i = 0; i < 100; i++)
std::cout << "Display Thread Excecuting" << std::endl;
}
};
int main() {
std::thread threadObj((DisplayThread()));
for (int i = 0; i < 100; i++)
std::cout << "Display From Main Thread " << std::endl;
std::cout << "Waiting For Thread to complete" << std::endl;
threadObj.join();
std::cout << "Exiting from Main Thread" << std::endl;
return 0;
}
线程间的区分
每个std::thread对象都有一个相关联的id,可以获取到
- std::thread::get_id() :成员函数中给出对应线程对象的id
- std::this_thread::get_id() : 给出当前线程的id
- 如果std::thread对象没有关联的线程,get_id()将返回默认构造的std::thread::id对象:“not any thread”
- std::thread::id也可以表示id
示例:
#include <iostream>
#include <thread>
void thread_function() {
std::cout << "inside thread :: ID = " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread threadObj1(thread_function);
std::thread threadObj2(thread_function);
if (threadObj1.get_id() != threadObj2.get_id()) {
std::cout << "Both Threads have different IDs" << std::endl;
}
std::cout << "From Main Thread :: ID of Thread 1 = " << threadObj1.get_id() << std::endl;
std::cout << "From Main Thread :: ID of Thread 2 = " << threadObj2.get_id() << std::endl;
threadObj1.join();
threadObj2.join();
return 0;
}
Join和Detaching
在任意一个时间点上,线程是可结合(joinable)或者是可分离的(detached)。一个可结合线程是可以被其他线程收回资源和杀关闭。在被回收之前,他的存储器资源(栈等)是不释放的。而对于detached状态的线程,其资源不能被别的线程收回和关闭,只有等到线程结束才能由系统自动释放。
Joining线程
线程一旦启动,另一个线程可以通过调用std::thread对象上调用join()函数等待这个线程执行完毕
std::thread th(funcPtr);
th.join();
看一个例子
主线程启动10个工作线程,启动完毕后,main函数等待他们执行完毕,joining完所有线程后,main函数继续执行
#include <vector>
#include <iostream>
#include <thread>
#include <algorithm>
#include <functional>
class WorkerThread
{
public:
void operator()(){
std::cout<<"Worker Thread "<<std::this_thread::get_id()<<"is Excecuting"<<std::endl;
}
};
int main(){
std::vector<std::thread> threadList;
for(int i = 0; i < 10; i++){
threadList.push_back(std::thread(WorkerThread()));
}
// Now wait for all the worker thread to finish i.e.
// Call join() function on each of the std::thread object
std::cout<<"Wait for all the worker thread to finish"<<std::endl;
std::for_each(threadList.begin(), threadList.end(), std::mem_fn(&std::thread::join));
std::cout<<"Exiting from Main Thread"<<std::endl;
return 0;
}
Detaching 线程
detach一个线程,需要在std::thread对象中调用std::detach()函数
std::thread th(funcPtr)
th.detach();
- 调用detach()后,std::thread对象不再与实际执行线程相关联
- 在线程句柄上调用detach() 和 join()要小心
case1:不要在没有关联执行线程的std::thread对象上调用join() 或 detach()
#include <vector>
#include <iostream>
#include <thread>
#include <algorithm>
#include <functional>
void thread_function() {
std::cout << "inside thread :: ID = " << std::this_thread::get_id() << std::endl;
}
int main(){
std::thread threadObj(thread_function);
threadObj.join();
threadObj.join();// It will cause Program to Terminate
return 0;
}
运行结果如下
inside thread :: ID = 140293418059520
terminate called after throwing an instance of 'std::system_error'
what(): Invalid argument
Aborted (core dumped)
当join()函数在线程对象上执行,当join()返回时,std::thread 对象与他没有关联线程,如果在这样的对象上再次调用join()函数,那么它将导致程序终止。
类似的,调用detach()使std::thread对象没有链接任何线程函数,在这种情况下,在一个std::thread对象上调用detach()函数两次将导致程序终止。
#include <vector>
#include <iostream>
#include <thread>
#include <algorithm>
#include <functional>
void thread_function() {
std::cout << "inside thread :: ID = " << std::this_thread::get_id() << std::endl;
}
int main(){
std::thread threadObj(thread_function);
threadObj.detach();
threadObj.detach();// It will cause Program to Terminate
return 0;
}
运行结果如下
terminate called after throwing an instance of 'std::system_error'
what(): inside thread :: ID = 140615339697920
Invalid argument
Aborted (core dumped)
因此,在每次调用join()或detach()前,需要检查线程是否join-able
#include <vector>
#include <iostream>
#include <thread>
#include <algorithm>
#include <functional>
void thread_function() {
std::cout << "inside thread :: ID = " << std::this_thread::get_id() << std::endl;
}
int main(){
std::thread threadObj(thread_function);
if(threadObj.joinable()){
std::cout<<"Detaching Thread"<<std::endl;
threadObj.detach();
}
if(threadObj.joinable()){
std::cout<<"Detaching Thread"<<std::endl;
threadObj.detach();
}
std::thread threadObj2(thread_function);
if(threadObj2.joinable()){
std::cout<<"Joining Thread"<<std::endl;
threadObj2.join();
}
if(threadObj2.joinable()){
std::cout<<"Joining Thread"<<std::endl;
threadObj2.join();
}
return 0;
}
执行结果如下
Detaching Thread
Joining Thread
inside thread :: ID = 139974543935232
inside thread :: ID = 139974535542528
case2:不要忘记使用关联的执行线程在std::thread对象上调用join或detach
如果std::thread关联的执行线程没有调用join和detach,那么在对象的析构期,它将终止程序。因为在析构期,它将检查线程是否仍然Join-able,如果是Joinable,就终止程序。
#include <iostream>
#include <thread>
#include <algorithm>
class WorkerThread
{
public:
void operator()()
{
std::cout << "Worker Thread" << std::endl;
}
};
int main()
{
std::thread threadObj((WorkerThread()));
//如果没有在std::thread对象上调用join或detach,其析构函数将会终止程序
return 0;
}
执行结果
terminate called without an active exception
Aborted (core dumped)
类似的,不能忘记在异常情况下调用join或detach,
传参给线程
要将参数传递给线程的可关联对象或函数,只需将参数传递给std::thread构造函数。
默认情况下,所有的参数都将复制到新线程的内部存储中。
看一个例子:
给线程传递单个参数
#include <iostream>
#include <string>
#include <thread>
void threadCallback(int x, std::string str) {
std::cout << "Passed Number = " << x << std::endl;
std::cout << "Passed String = " << str << std::endl;
}
int main() {
int x = 10;
std::string str = "Sample String";
std::thread threadObj(threadCallback, x, str);
threadObj.join();
return 0;
}
怎样给线程传递函数
不要将本地堆栈变量的地址传递给线程的回调函数,因为线程1中的局部变量可能会超出范围,但线程2仍然尝试通过它的地址访问它。在这种情况下,访问无效地址可能会导致不可预测的行为。
例如:
#include <iostream>
#include <thread>
void newThreadCallback(int* p) {
std::cout << "Inside Thread : "" : p = " << *p << std::endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
*p = 19;
}
void startNewThread() {
int i = 10;
std::cout << "Inside Main Thread : "" : i = " << i << std::endl;
std::thread t(newThreadCallback, &i);
t.detach();
std::cout << "Inside Main Thread : "" : i = " << i << std::endl;
}
int main() {
startNewThread();
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
return 0;
}
执行结果
Inside Main Thread : : i = 10
Inside Main Thread : : i = 10
Inside Thread : : p = 0
可以发现主线程先退出,因此变量i
被销毁,另一个线程访问到的值就是0(未初始化)。
对比以下的程序。
#include <iostream>
#include <thread>
void newThreadCallback(int* p) {
std::cout << "Inside Thread : "" : p = " << *p << std::endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
*p = 19;
}
void startNewThread() {
int i = 10;
std::cout << "Inside Main Thread : "" : i = " << i << std::endl;
std::thread t(newThreadCallback, &i);
t.detach();
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
std::cout << "Inside Main Thread : "" : i = " << i << std::endl;
}
int main() {
startNewThread();
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
std::cout<<"exiting"<<std::endl;
return 0;
}
执行结果
Inside Main Thread : : i = 10
Inside Thread : : p = 10
Inside Main Thread : : i = 19
exiting
同样的,在将指针传递给位于heap上的线程时,要小心,因为某些线程可能在新线程尝试访问它之前删除该内存。在这种情况下,访问无效地址可能会导致不可预测的行为。
例如:
#include <iostream>
#include <thread>
void newThreadCallback(int* p) {
std::cout << "Inside Thread : "" : p = " << *p << std::endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
*p = 19;
}
void startNewThread() {
int* p = new int();
*p = 10;
std::cout << "Inside Main Thread : "" : *p = " << *p << std::endl;
std::thread t(newThreadCallback, p);
t.detach();
delete p;
p = NULL;
}
int main() {
startNewThread();
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
return 0;
}
输出结果是
Inside Main Thread : : *p = 10
Inside Thread : : p = 0
可见p所指向的内存已经没有值了。
给线程传递引用
由于参数被复制到新的线程堆栈,所以,如果想通过常用的方式传递引用,如:
#include <iostream>
#include <thread>
void threadCallback(int const& x) {
int& y = const_cast<int&>(x);
y++;
std::cout << "Inside Thread x = " << x << std::endl;
}
int main() {
int x = 9;
std::cout << "In Main Thread : Before Thread Start x = " << x << std::endl;
std::thread threadObj(threadCallback, x);
threadObj.join();
std::cout << "In Main Thread : After Thread Joins x = " << x << std::endl;
return 0;
}
输出为:
In Main Thread : Before Thread Start x = 9
Inside Thread x = 10
In Main Thread : After Thread Joins x = 9
即使threadCallback接受参数作为引用,但是并没有改变main中x的值,在线程引用外它是不可见的。这是因为线程函数threadCallback中的x是引用复制在新线程的堆栈中的临时值。
如何修改:
使用 std::ref
#include <iostream>
#include <thread>
void threadCallback(int const& x) {
int& y = const_cast<int&>(x);
y++;
std::cout << "Inside Thread x = " << x << std::endl;
}
int main() {
int x = 9;
std::cout << "In Main Thread : Before Thread Start x = " << x << std::endl;
std::thread threadObj(threadCallback, std::ref(x));
threadObj.join();
std::cout << "In Main Thread : After Thread Joins x = " << x << std::endl;
return 0;
}
输出:
In Main Thread : Before Thread Start x = 9
Inside Thread x = 10
In Main Thread : After Thread Joins x = 10
指定一个类的成员函数的指针作为线程函数
将指针传递给成员函数作为回调函数,并将指针指向对象作为第二个参数
#include <iostream>
#include <thread>
class DummyClass {
public:
DummyClass() { }
DummyClass(const DummyClass& obj) { }
void sampleMemberfunction(int x) {
std::cout << "Inside sampleMemberfunction " << x << std::endl;
}
};
int main() {
DummyClass dummyObj;
int x = 10;
std::thread threadObj(&DummyClass::sampleMemberfunction, &dummyObj, x);
threadObj.join();
return 0;
}