STL线程库简介

STL线程库的前身是boost::thread,在C++ 11标准化后便正式归纳入了stl库,通过它我们可以很容易实现跨平台的线程管理。

线程管理

在std::thread库中,一个线程用的是一个thread对象表示,当创建一个thread对象时即创建一个线程,一个简单的示例如下:

    #include <iostream>
    #include <thread>
    using namespace std;

    void thread_entry(const char* arg)
    {
        cout << "thread "<<
this_thread::get_id() << " created: " << arg << endl;
    }

    int main()
    {
        thread
thrd(thread_entry, "hello world");
        
thrd.join();
        return 0;
    }

这里我通过thread对象创建了一个线程,并调用join函数等待线程结束。thead对象的构造函数中是可以传入参数的,非常方便。

std::this_thread名字空间

在前面的例子中,还用到了一个this_thread::get_id()函数用以获取当前线程的tid,std::this_thread名字空间提供了如下函数以管理当前线程:

  • yield
  • get_id
  • sleep_for
  • sleep_until

基本上从名字里就可以猜出它们的功能了。以其中的sleep_for函数为例,它提供了一个跨平台的sleep功能,再也不用自己封装了:

    std::chrono::milliseconds dura(2000);
    std::this_thread::sleep_for(dura);

PS:在gcc中使用这个函数时,需要再编译的时候加-D_GLIBCXX_USE_NANOSLEEP选项,否则报语法错误,具体原因可以参看这篇文章

Linux平台的运行错误

上述代码在Windows平台运行良好,但在Linux上运行的时候发现如下错误:

    tianfang > run
    terminate called after throwing an instance of 'std::system_error'
        what(): Operation not permitted
    Aborted
    tianfang >

然后在StackOverFlow上找到了答案:在链接的时候要加 –pthread 选项。这个应该算个bug了。

删除Thread对象

我最初以为当thread对象删除时会自动杀掉线程,运行时却发现:thread对象删除时,如果没有调用join或attach,就会报异常。为了演示这一过程,首先我们把main函数改成如下所示:

    int main()
    {
        thread thrd(thread_entry, "hello world");
        //thrd.join();
        return 0;
    }

运行该代码时,就会发现如下错误:

    tianfang > run
    terminate called without an active exception
    Aborted
    tianfang >

也就是说,thread对象并不会自动管理线程结束,需要手动控制。常见的控制手段有两个,join和detach。join已经介绍过,用于等待线程结束,而detach的功能是托管线程,线程仍然继续运行至结束,但不再受到thread对象控制。

对线程编程模型比较熟悉的人可能会发现:它并没有提供一个强制终止线程的选项。在boost的this_thread名字空间下其实是提供了终止线程的函数的,但是并没有纳入stl中,可见标准委员会也是不建议强制终止这种不健壮的做法。

互斥和同步

互斥体

stl中对mutex划分得很细,按是否递归、是否超时分为四个对象:

  • mutex
  • timed_mutex
  • recursive_mutex
  • recursive_timed_mutex

这四个对象的操作函数大体上都是如下几个:

  • lock    获取锁
  • trylock    尝试获取锁
  • unlock    释放锁
  • try_lock_for    在一定时间范围内尝试获取锁(有超时功能的mutex才能用)
  • try_lock_until    尝试获取锁到某个时间点位置(有超时功能的mutex才能用)

除了直接使用这几个类外,也可以使用lock_guar、unique_lock之类的的封装类,它的功能上类似C#的lock关键字(lock范围并不等价),在指定范围内自动获取锁,出了该范围外自动解锁,可有效防止加解锁不对称。

另外,stl也提供了try_lock和lock的泛型方法实现批量获取锁定,这里就不多介绍了。

条件变量

stl中对条件变量封装的是condition_variable类,它的基本使用方式和系统api差不多,如下是一个生产者/消费者的例子:

    #include <condition_variable>
    #include <mutex>
    #include <thread>
    #include <iostream>
    #include <queue>
    #include <chrono>
    using namespace std;

    int main()
    {
        queue<int> buffer;
        mutex m;
        condition_variable cond_var;
        int num = 0;

        thread producer([&]()
        {
            while (true)
            {
                this_thread::sleep_for(chrono::seconds(1));

                unique_lock<std::mutex> lock(m);

                num++;
                std::cout << "producing " << num << '\n';
                buffer.push(num);

                cond_var.notify_one();
            }
        });

        thread consumer([&]()
        {
            while (true)
            {
                unique_lock<std::mutex> lock(m);

                if(buffer.empty())
                    cond_var.wait(lock);

                std::cout << "consuming " << buffer.front() << '\n';
                buffer.pop();
            }
        });

        producer.join();
        consumer.join();
    }

条件变量还有其它几个封装,这里就不多介绍了。

其它

其它几个同步互斥的类,如栅栏、读写锁等并没有纳入STL,如果要使用它们,可以直接使用boost库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值