cpp多线程编程demo1

本文介绍了C++11引入的线程库,讲解如何创建和管理线程,包括使用函数指针、函数对象和Lambda函数创建线程,线程的join和detach操作,以及线程间通信的基础概念。示例代码详细展示了线程的创建、同步和资源管理,强调了线程安全和避免资源泄露的重要性。
摘要由CSDN通过智能技术生成

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对象附加一个回调,当这个新线程启动时,它将被执行。 这些回调可以是,

  1. 函数指针
  2. 函数对象
  3. 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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值