c++11 多线程 -- 基本使用

c++11 多线程 – 基本使用

前言:这篇文章仅针对没有使用过c++11线程库的童鞋来快速入门,也是自己的一个简单记录,内容比较基础。

-1.线程的基本使用

-2.互斥量

-3.条件变量

-4.原子变量


1.线程的基本使用

代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <thread>
#include <iostream>

int k = 0;

void fun(void)
{
    //线程休眠,chrono是c++11的时间相关库。
          std::this_thread::sleep_for(std::chrono::seconds(3));
    for(int i = 0; i < 10; ++i)
    {
        std::cout << "hello world" << std::endl;
        k++;
    }
}

int main(int argc, char *argv[])
{
    //创建线程对象
    std::thread t1(fun);
    //输出线程id和cpu核数
    std::cout << "ID:" << t1.get_id() << std::endl;
    std::cout << "CPU:" << std::thread::hardware_concurrency() << std::endl;
    //主函数阻塞等待线程结束
    t1.join();
    //主函数和线程函数分离执行,线程变为后台线程
    //t1.detach();

    std::cout << k << std::endl;

    return EXIT_SUCCESS;
}

注意:

1.linux下用gcc或clang必须加-pthread连接到线程库,否则会出错。

2.主线程函数不能提前结束于新创建的线程函数,因为在c++11中,线程也是对象,主函数结束线程对象即销毁。

3.t.join()是主函数阻塞等待线程结束才能结束,主函数会继续执行,并阻塞在return处

t.detach()主函数和线程函数分离,各自执行各自的,线程变为后台线程。

4.可通过bind和lambda创建线程

可以将线程保存在容器中,以保证线程对象的声明周期。

但是注意线程没有拷贝构造函数,有移动构造函数。

图上可以看出拷贝构造函数为delete。


2.互斥量

分为4种

std::mutex 独占的互斥量,不能递归使用

std::timed_mutex 带超时的独占的互斥量,不能递归使用

std::recursive_mutex 递归互斥量,不带超时功能

std::recursive_timed_mutex 带超时的递归互斥量

代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
#include <thread>
#include <mutex>

std::mutex g_lock;
int i = 0;

void func(void)
{
    //使用RAII手法,在离开作用域时自动释放
    std::lock_guard<std::mutex>locker(g_lock);
    //正常的互斥锁上锁
    //g_lock.lock();
    i++;
    std::cout << i << std::endl;
    //互斥锁解锁
    //g_lock.unlock();
}

int main(int argc, char *argv[])
{
    std::thread t1(func);
    std::thread t2(func);
    std::thread t3(func);
    t1.join();
    t2.join();
    t3.join();

    return EXIT_SUCCESS;
}

注意:

1.多次获取互斥量可能会发生死锁,所以我们调用std::recursive_mutex递归锁,允许同一线程多次获得该锁,一般不要使用递归锁,原因:<1.用到递归锁会使得程序的逻辑变复杂,使用到递归锁的程序一般可以简化。<2.递归锁比非递归锁效率低。<3.递归锁的可重入次数是有限的,超过也会报错。

2.可以使用带超时时间的互斥锁,避免阻塞在等待互斥锁上。

3.unique_lock: 是一个通用的互斥量封装类。与lock_guard不同,它还支持延迟加锁、时间锁、递归锁、锁所有权的转移并且还支持使用条件变量。这也是一个不可复制的类,但它是可以移动的类。


3.条件变量

阻塞一个或多个线程,直到收到另外一个线程发来的通知或者超时,才会唤醒当前阻塞的进程

条件变量需要和互斥量配合使用

c++11提供了两种条件变量

1.std::condition_variable,配合std::unique_lock进行wait操作

2.std::condition_variable_any,和任意带有lock,unlock的mutex进行搭配使用,比较灵活但效率略低。

条件变量的wait还有一个重载的方法,可以设置一个条件,条件变量会先检查判断式是否满足条件。

原理:

当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。

代码:用c++11多线程实现同步队列

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
#include <list>
#include <vector>
#include <memory>
#include <unistd.h>

template<typename T>
class SynQueue
{
    public:
        //构造函数
        SynQueue(int MaxSize):
            m_maxsize(MaxSize) { }

        //将T类型对象放入队列
        void Put(const T&x)
        {
            std::lock_guard<std::mutex>locker(m_mutex);
            while(isFull())
            {
                //如果满了,等待
                m_notFull.wait(m_mutex);
            }
            m_queue.push_back(x);
            //通过条件变量唤醒一个线程,也可以所有线程
            m_notEmpty.notify_one();
        }

        //将T类型对象从队列取出
        void Take(T&x)
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            while(isEmpty())
            {
                std::cout << "no resource... please wait" << std::endl;
                m_notEmpty.wait(m_mutex);
            }
            x = m_queue.front();
            m_queue.pop_front();
            m_notFull.notify_one();
        }

        //判断队列是否为空
        bool Empty()
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            return m_queue.empty();
        }

        //判断队列是否为满
        bool Full()
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            return m_queue.size() == m_maxsize;
        }

        //返回队列大小
        size_t Size()
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            return m_queue.size();
        }

    private:
        //判断空或满,内部使用不需要加锁
        bool isFull() const
        {
            return m_queue.size() == m_maxsize;
        }
        bool isEmpty() const
        {
            return m_queue.empty();
        }

    private:
        //队列
        std::list<T>m_queue;
        //互斥锁
        std::mutex m_mutex;
        //不为空时的条件变量
        std::condition_variable_any m_notEmpty;
        //不为满时的条件变量
        std::condition_variable_any m_notFull;
        //队列最大长度
        int m_maxsize;
};

void func(SynQueue<int> *sq)
{
    int ret;
    sq->Take(ret);
    std::cout << ret << std::endl;
}

int main(int argc, char *argv[])
{
    //创建线程队列,长度最大为20
    SynQueue<int>syn(20);
    //放置数据对象
    for(int i = 0; i < 10; i++)
    {
        syn.Put(i);
    }
    std::cout << syn.Size() << std::endl;

    //线程不能拷贝,用容器和智能指针来管理线程生存
    std::vector<std::shared_ptr<std::thread>> tvec;
    //多循环一次,资源不足,阻塞最后一个线程,在后面添加一个资源,看该线程是否会被唤醒执行。
    for(int i = 0; i < 11; i++)
    {
        //创建线程并且将管理线程的智能指针保存到容器中
        tvec.push_back(std::make_shared<std::thread>(func, &syn));
        //变为后台线程
        tvec[i]->detach();
    }
    sleep(10);
    //添加一个资源
    syn.Put(11);
    sleep(10);

    return EXIT_SUCCESS;
}

运行结果:


4.原子变量

原子变量,为原子操作,不需要加锁

std::atomic<T>

详情可参考,这里仅简单举例用法

cppreference atomic

代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>

//创建int类型的原子变量
std::atomic<int>atc(0);

void func()
{
    std::cout << atc << std::endl;
    原子变量自增
    atc++;
}

int main(int argc, char *argv[])
{
    std::vector<std::thread>tvec;
    for(int i = 0; i < 10; i++)
    {
        std::thread t(func);
        //线程对象移动语义
        tvec.push_back(std::move(t));
        tvec[i].join();
    }

    return EXIT_SUCCESS;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-05 07:52:52

c++11 多线程 -- 基本使用的相关文章

C++11多线程教学(二)

转载自:http://www.cnblogs.com/lidabo/p/3908713.html C++11多线程教学II 从我最近 发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭 建相当复杂的处理图片程序,但是我们回避了线程同步的议题.在接下来的部分,我们将进入C++11多线程编程的同步领域,看看如何来同步一组并行的线程. 我们快速回顾一下如何利用c++11创建线程组.上次教学当中,我们用传统c数

C++11多线程教学(一)

转载自:http://www.cnblogs.com/lidabo/p/3908705.html 本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://solarianprogrammer.com/2011/10/12/cpp-11-regex-tutorial/) 2. raw string(http://solarianprogrammer.com/

C++11多线程编程之互斥量

+(UIImage*)createImageFromView:(UIView*)view { //obtain scale CGFloat scale = [UIScreen mainScreen].scale; 开始绘图,下面方法,第一个参数表示区域大小.第二个参数表示是否是非透明的.如果需要显示半透明效果,需要传NO,否则传YES.第三个参数就是屏幕密度了 UIGraphicsBeginImageContextWithOptions(CGSizeMake(view.frame.size.wi

C++11 多线程

C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等,使用起来都比较复杂,C++11提供了新头文件<thread>.<mutex>.<atomic>.<future>等用于支持多线程. 使用C++11开启一个线程是比较简单的,下面来看一个简单的例子: #include <thread> #include &

Linux下c++11多线程聊天室

刚看的c++11多线程,写个聊天室试试编译的时候加上 c++11 和 多线程库g++ -Wall -std=c++0x -pthread -o server server.cppserver 和client 都是 q 退出?1. [代码]server.cpp #include <iostream>#include <thread>#include <arpa/inet.h>#include <cstring>#include <vector>#i

C++11 并发指南一(C++11 多线程初探)(转)

引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧,和大家共勉. 相信 Linux 程序员都用过 Pthread, 但有了 C++11 的 std::thread 以后,你可以在语言层面编写多线程程序了,直接的好处就是多线程程序的可移植性得到了很大的提高,所以作为一名 C++ 程序员,熟悉 C++11 的多线程编程方式还是很有益处的. 如果你对 C+

DirectX* 11 多线程渲染的性能、方法和实践

对于在 CPU 上运行的 PC 游戏,渲染通常是主要的性能瓶颈:多线程渲染是一种消除瓶颈的有效方法.本文研究了 DirectX* 11 多线程渲染的性能可扩展性,讨论了多线程渲染的两种基本方法,并介绍了传统多线程延迟着色管线在大型在线游戏<战意*>中的应用.了解更多 原文地址:https://www.cnblogs.com/IDZPRC/p/10912669.html

11.多线程&amp;&amp;并发

11.1 操作系统中线程和进程的概念 一些常见的概念: 程序:指令和数据的byte序列,eg:qq.exe;a2. 进程:正在运行的程序(如QQ);a3.一个进程中可能有一到多个线程. 线程的概念:Thread 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.进程也可能是整个程序或者是部分程序的动态执行.线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行.也可以把它理解为代码运行的上下文.所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务.通常由操作系统负责

java 22 - 11 多线程之模拟电影院售票口售票

使用多线程实现的第二种方式: 首先创建自定义类 1 public class SellTicket implements Runnable { 2 // 定义100张票 3 private int ticket = 100; 4 5 public void run() { 6 7 //假设一直在售票 8 while(true){ 9 //现实中买票时,都会有延迟的,所以让线程休息下 10 try { 11 Thread.sleep(100); 12 } catch (InterruptedExc