上周在看C++ share_ptr时,有段关于讲多线程的代码很精巧,C++线程库起一个线程,只需要创建一个std::thread对象,有些ide没有相应的库,可以在linux平台下编译时加-pthread参数即可
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
struct Base
{
Base() { std::cout << " Base::Base()\n"; }
// 注意:此处非虚析构函数 OK
~Base() { std::cout << " Base::~Base()\n"; }
};
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
void thr(std::shared_ptr<Base> p)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << "local pointer in a thread:\n"
<< " lp.get() = " << lp.get()
<< ", lp.use_count() = " << lp.use_count() << '\n';
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
std::cout << "Created a shared Derived (as a pointer to Base)\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
std::thread t1(thr, p), t2(thr, p), t3(thr, p);
p.reset(); // 从 main 释放所有权
std::cout << "Shared ownership between 3 threads and released\n"
<< "ownership from main:\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
t1.join(); t2.join(); t3.join();
std::cout << "All threads completed, the last one deleted Derived\n";
}
相应日志
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
p.get() = 0x23b8c30, p.use_count() = 1
Shared ownership between 3 threads and released
ownership from main:
p.get() = 0, p.use_count() = 0
local pointer in a thread:
lp.get() = 0x23b8c30, lp.use_count() = 4
local pointer in a thread:
lp.get() = 0x23b8c30, lp.use_count() = 4
local pointer in a thread:
lp.get() = 0x23b8c30, lp.use_count() = 2
Derived::~Derived()
Base::~Base()
还有mutex相关的应用,程序锁一直是操作系统中线程对某对象上锁的机制,这里的lock_guard是互斥封装器,接收给定的所有权,这里的作用就是对共享智能指针完成读保护机制,当控制离开mutex创建作用域后,lock_guard自动销毁,mutex被释放
标准线程库有两种等待线程执行结束的方式:
1.detach,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。
2.join,等待启动的线程完成,才会继续往下执行
可以理解为,等到join才是线程的析构函数,所以运行上述代码的时候,会出现use_count连续打印4的状况,因为,可能一个线程结束完毕,在还未销毁一个线程的情况下,另一个线程执行,计数为4
代码改写
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
#include <cstring>
struct Base
{
Base() { std::cout << " Base::Base()\n"; }
// 注意:此处非虚析构函数 OK
~Base() { std::cout << " Base::~Base()\n"; }
};
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
void thr(std::shared_ptr<Base> p, std::string s)
{
//std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << s <<std::endl;
std::shared_ptr<Base> lp = p; // 线程安全,虽然自增共享的 use_count
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << "local pointer in a thread:\n"
<< " lp.get() = " << lp.get()
<< ", lp.use_count() = " << lp.use_count() << '\n';
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
std::cout << "Created a shared Derived (as a pointer to Base)\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
std::string x = "x", y = "y", z = "z";
std::thread t1(thr, p , x), t2(thr, p, y), t3(thr, p ,z);
p.reset(); // 从 main 释放所有权
t3.join();
std::cout << "Shared ownership between 3 threads and released\n"
<< "ownership from main:\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
t1.join(); t2.join();
std::cout << "All threads completed, the last one deleted Derived\n";
}
相应日志
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
p.get() = 0x6d1c30, p.use_count() = 1
x
y
local pointer in a thread:
lp.get() = 0x6d1c30, lp.use_count() = 5
z
local pointer in a thread:
lp.get() = 0x6d1c30, lp.use_count() = 4
Shared ownership between 3 threads and released
ownership from main:
p.get() = 0, p.use_count() = 0
local pointer in a thread:
lp.get() = 0x6d1c30, lp.use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived
在最初的代码中先打印p,是因为其他三个线程都sleep了1s,不是因为有线程阻塞机制,线程需要抢夺控制台,所以在打印时间没有阻塞的部分有抢夺输出的情况这也进一步说明了mutex的机制
join是阻塞到这个线程结束,才执行主函数下面的语句,只保证,在到此句前线程结束,线程和主函数竞争,线程在创建的时候就已经开始执行,先创建的线执行。
另外关于use_count多1。的情况应该是因为线程传入p指针的时候,作为形式参数调用了拷贝构造函数。