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;
}

补充:

1.线程创建函数能够为各种可调用类型,函数指针,仿函数,lambda表达式。

可是注意,线程參数会以默认的方式被拷贝到内部存储空间中。即便函数中的对应參数期待引用。线程运行函数内部能够訪问它们。

可能我们想传递引用在线程运行函数内部改动并返回结果,但复制会导致达不到预期的效果,解决方式是传递引用參数时用std::ref来包装或者传递指针。


2.线程初始化运行函数也能够传递一个成员函数

std::thread tid(&x::func, &my_func)


3.线程不支持拷贝,但支持全部权移动。


4.假设线程运行函数使用了暂时变量可能会出现故障。线程调用了detach在后台运行时,暂时变量可能已经销毁,那么线程会訪问已经被销毁的变量。join能保证。

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

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

    C++11多线程教学II 从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程 ...

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

    本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...

  3. C++11多线程教学II

    从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程序,但是我们回避了线程同步 ...

  4. c++ 11 多线程教学(1)

    本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...

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

    引言 C++ 11自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些C++ 11的新特性,算是记录一下自己学到的东西吧,和大家共勉. 相信Linux程序员都用过Pthrea ...

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

    引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...

  7. C++11 多线程编程 使用lambda创建std::thread (生产/消费者模式)

    要写个tcp server / client的博客,想着先写个c++11多线程程序.方便后面写博客使用. 目前c++11中写多线程已经很方便了,不用再像之前的pthread_create,c++11中 ...

  8. C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)

    前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题——生产者消费者模型,并给出 ...

  9. C++11多线程教学

    转自:http://www.cnblogs.com/lidabo/p/3908705.html 本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads ...

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

    刚看的c++11多线程,写个聊天室试试编译的时候加上 c++11 和 多线程库g++ -Wall -std=c++0x -pthread -o server server.cppserver 和cli ...

随机推荐

  1. JAVA基础实例(二)

    1.做一个饲养员给动物喂食物的样例体现JAVA中的面向对象思想,接口(抽象类)的用处 package com.softeem.demo; /** *@authorleno *动物的接口 */ inte ...

  2. css3新特性选择器(补充)

    1.选择p标签中的第一个字符 p:first-letter{ color:red; font-size:25px; } 2.选择p标签中的第一行 p:first-line{ color:red; fo ...

  3. dhclient---获取动态IP

    dhclient命令使用动态主机配置协议动态的配置网络接口的网络参数. 语法 dhclient(选项)(参数) 选项 0:指定dhcp客户端监听的端口号: -d:总是以前台方式运行程序: -q:安静模 ...

  4. 003 python 注释/数据类型/运算符/输入输出/格式化输出

    集成开发环境 pycharm 工欲善其事,必先利其器 pycharm是具备一般的python ide的功能,同时呢支持调试,语法高亮,代码管理,智能提示 加快快发的速度,提高开发效率 注释 what ...

  5. Cocos2d-x 之大牛看法

    (未完毕) cocos2d-x并非一个适合网游client(mmo)的游戏引擎.越是大型游戏,这个小引擎就越无法驾驭(尽管它很受欢迎). 之前我在原来的公司使用的是自主研发的C3引擎,已经对外开放(尚 ...

  6. vue ---webpack 打包上线

     先来描述一下期间遇到的问题有哪些: 1.打包后将 dist 文件夹和 index.html 放到 tomcat,在浏览器中访问时,出现空白页,f12 提示 404. 2.打包好的静态资源均是绝对路径 ...

  7. 洛谷 P3386 【模板】二分图匹配 Dinic版

    题目背景 二分图 题目描述 给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数 输入输出格式 输入格式: 第一行,n,m,e 第二至e+1行,每行两个正整数u,v,表示u,v有一条连边 ...

  8. Express简介、安装

    Express 基于Node.js平台,快速.开放.极简的web开发框架,是目前最流行的基于Node.js的web开发框架,它提供一系列强大的功能,比如: 路由控制 参数获取 send和sendFil ...

  9. BZOJ2555: SubString(后缀自动机,LCT维护Parent树)

    Description 懒得写背景了,给你一个字符串init,要求你支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 你必须在线支 ...

  10. Django路由分配以及模版渲染

    路由上: 在网络上区分不同的电脑通过IP.端口和网卡的MAC地址等,在web框架中怎么区分不同的请求呢,就是通过 ‘url(路由)’ ,url 学名叫做全球统一资源定位符,其实就是一个网址 一个url ...