简单总结下C++自带的 std::thread
优点:
1. 简单,易用
2. 跨平台,pthread只能用在POSIX系统上(其他系统有其独立的thread实现)
3. 提供了更多高级功能,比如future
4. 更加C++(跟匿名函数,std::bind,RAII等C++特性更好的集成

缺点:
1. 没有RWlock。有一个类似的shared_mutex,不过它属于C++14,你的编译器很有可能不支持。
2. 操作线程和Mutex等的API较少。毕竟为了跨平台,只能选取各原生实现的子集。如果你需要设置某些属性,需要通过API调用返回原生平台上的对应对象,再对返回的对象进行操作。

joinable函数
检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程 已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。

t.joinable();

native_handle函数
由于 std::thread 的实现和操作系统相关,因此该函数返回与 std::thread 具体实现相关的线程句柄,例如在符合 Posix 标准的平台下(如 Unix/Linux)是 Pthread 库

yield函数
当前线程放弃执行,操作系统调度另一线程继续执行。

std::this_thread::yield();

hardware_concurrency 函数
此函数为静态函数,用于检测硬件并发特性,返回当前平台的线程实现所支持的线程并发数目,但返回值仅仅只作为系统提示(hint)。

条件变量
作为线程同步的重要方法,std::thread,也支持了condition_variable:当某些条件不满足时,停止执行直到该条件被满足。

在经典的生产者消费者模式下,生产者和消费者就是通过condition variable来实现同步的。当有限的生产力无法满足日益增长的消费需求时,消费者进程就会去睡一觉,直到它想要的东西生产出来才醒来

condition_variable需要和 unique_lock搭配使用。在一个线程调用wait之前,它必须持有unique_lock锁。当wait被调用时,该锁会被释放,线程会陷入沉睡,等待着王子生产者发过来的唤醒信号。当生产者调用同一个condition_variable的 notify_all方法时,所有沉睡在该变量前的消费者会被唤醒,并尝试重新获取之前释放的unique_lock,继续执行下去。(注意这里发生了锁争用,只有一个消费者能够获得锁,其他消费者得等待该消费者释放锁)。如果只想叫醒一个线程,可以用notify_one

wait可以接受两个参数。此时第二个参数用于判断当前是否要沉睡

[]{ return msgQueue.size() > 0;});

while (msgQueue.size() <= 0) {
    condvar.wait()
}

这里把沉睡的线程比作睡美人,万一王子变成了青蛙,来不及唤醒她,那睡美人不就得睡到天荒地老海枯石烂?

为了解决这个问题,通过wait_until和wait_for,你可以设定线程的等待时间。设置notify_all_at_thread_exit也许能帮得上忙。

atomic
它提供了一组轻量级的、作用在单个变量上的原子操作。atomic位于头文件atomic。内容不会被线程的切换所打断。

/*time:20180605 author:MISAYAONE */
#include<iostream>
#include<thread>
#include<Windows.h>
#include<mutex>
#include<condition_variable>
#include<queue>
using namespace std;

mutex mu_msg;
condition_variable cv_msg;
//消费者,生产者模式的简单实现(利用条件变量和互斥锁)
queue<int> msg_queue;
void producer(int start,int end)
{
    for (int i=start;i<end;i++)
    {
        mu_msg.lock();
        msg_queue.push(i);
        mu_msg.unlock();
    }
    cout << "msg produced" << endl;
    if (msg_queue.size() > 20)
    {
        cv_msg.notify_all();//沉睡在该变量上的所有线程都将被唤醒,并尝试获取互斥锁(只有一个消费者可获得互斥锁)
    }
}

void consumer(int demand)
{
    while (true)
    {
        unique_lock<std::mutex> ulock(mu_msg);
        cv_msg.wait(ulock,[] {return msg_queue.size() > 0;});//尝试获取互斥锁,否则等待
        printf("Consume msg %d\n",msg_queue.front());
        msg_queue.pop();
        --demand;
        if (!demand)
        {
            break;
        }
    }
}

mutex mu;//互斥锁对象

int num = 100;
void thread_func1()
{
    for (int i=0;i<10;i++)
    {
        cout <<"thread 1's "<< i << endl;
        Sleep(100);
    }

    while (num>0)//这里不能用while,好无知....
    {
        mu.lock();//互斥锁加锁
        cout << "1还在执行" << num << endl;
        num--;
        mu.unlock();//互斥锁解锁
        if (num < 50)
        {
            //当前线程就不再执行
            this_thread::yield();
            break;
        }
    }




}

void thread_func2(int n)
{
    for (int i = 0; i<n; i++)
    {
        cout <<"thread 2's "<< i << endl;
        Sleep(200);
    }
    while (num>0)
    {
        mu.lock();
        cout <<"2还在执行"<< num << endl;
        num--;
        mu.unlock();
    }

}

class A
{
public :
        void thread_func3()
        {
            for (int i = 0; i<10; i++)
            {
                cout << "class A thread 3's " << i << endl;
                Sleep(200);
            }
        }

};

class thread_guard
{
    thread &t;
public:
    explicit thread_guard(thread& _t):t(_t){}
    ~thread_guard()
    {
        if (t.joinable())
        {
            t.join();
        }
    }
    //拷贝赋值默认去除
    thread_guard(const thread_guard&) = delete;
    thread_guard& operator=(const thread_guard&) = delete;
};

int main()
{
    A a;
    thread task00();//默认构造函数

    thread task01(thread_func1);//正常构造函数
    thread_guard g(task01);//出现异常后,函数退出,g局部对象对销毁,此时g调用析构函数,会自动执行task01的join函数,从而能够保证join一定会被调用
    thread task02(thread_func2,10);//带args的线程构造函数
    //thread task_i(move(task02));//转移线程所有权,此时再利用task02调用join和detach会报错
    thread task03(&A::thread_func3,a);//传入类的成员函数作为线程构造函数的参数

    if (task01.joinable())//此处不可以使用task00线程来判断
    {
        cout << "task01 is joinable" << endl;
    }


    //join函数顺序执行线程
    task01.join();
    task02.join();
    //task_i.join();
    //task03.join();

    //不影响当前代码的执行,几个线程后台自行执行,不阻塞主线程
    //task01.detach();
    //task02.detach();
    //task_i.detach();
    task03.detach();

    //lambda匿名函数
    for (int j=0;j<20;j++)
    {
        thread t([j]() {cout << j << endl; });
        t.detach();
    }

    for (int i = 0; i<10; i++)
    {
        cout << "main thread's " << i << endl;
        Sleep(200);
    }

    cout << "task01.get_id() = " << task01.get_id() << endl;//获取线程ID

    unsigned int core_num = std::thread::hardware_concurrency();
    cout << "cpu 核数" << core_num << endl;

    thread mutiple00(producer,10,20);
    thread mutiple01(producer,20,30);
    thread mutiple02(producer,30,40);
    thread mutiple03(consumer,5);
    thread mutiple04(consumer,8);

    mutiple00.join();
    mutiple01.join();
    mutiple02.join();
    mutiple03.join();
    mutiple04.join();


    cin.get();
    return 0;
}

Windows 多线程编程入门2的更多相关文章

  1. iOS:核心图像和多线程应用程序

    我试图以最有效的方式运行一些核心图像过滤器.试图避免内存警告和崩溃,这是我在渲染大图像时得到的.我正在看Apple的核心图像编程指南.关于多线程,它说:“每个线程必须创建自己的CIFilter对象.否则,你的应用程序可能会出现意外行为.”这是什么意思?我实际上是试图在后台线程上运行我的过滤器,所以我可以在主线程上运行HUD(见下文).这在coreImage的上下文中是否有意义?

  2. ios – 多个NSPersistentStoreCoordinator实例可以连接到同一个底层SQLite持久性存储吗?

    我读过的关于在多个线程上使用CoreData的所有内容都讨论了使用共享单个NSPersistentStoreCoordinator的多个NSManagedobjectContext实例.这是理解的,我已经使它在一个应用程序中工作,该应用程序在主线程上使用CoreData来支持UI,并且具有可能需要一段时间才能运行的后台获取操作.问题是NSPersistentStoreCoordinator会对基础

  3. ios – XCode断点应该只挂起当前线程

    我需要调试多线程错误.因此,为了获得生成崩溃的条件,我需要在代码中的特定点停止一个线程,并等待另一个线程到达第二个断点.我现在遇到的问题是,如果一个线程遇到断点,则所有其他线程都被挂起.有没有办法只停止一个线程,让其他线程运行,直到它们到达第二个断点?)其他更有趣的选择:当你点击第一个断点时,你可以进入控制台并写入这应该在该断点处暂停当前上下文中的线程一小时.然后在Xcode中恢复执行.

  4. ios – 在后台线程中写入Realm后,主线程看不到更新的数据

    >清除数据库.>进行API调用以获取新数据.>将从API检索到的数据写入后台线程中的数据库中.>从主线程上的数据库中读取数据并渲染UI.在步骤4中,数据应该是最新数据,但我们没有看到任何数据.解决方法具有runloops的线程上的Realm实例,例如主线程,updatetothelatestversionofthedataintheRealmfile,因为通知被发布到其线程的runloop.在后台

  5. ios – NSURLConnectionLoader线程中的奇怪崩溃

    我们开始看到我们的应用启动时发生的崩溃.我无法重现它,它只发生在少数用户身上.例外情况是:异常类型:EXC_BAD_ACCESS代码:KERN_INVALID_ADDRESS位于0x3250974659崩溃发生在名为com.apple.NSURLConnectionLoader的线程中在调用时–[NSBlockOperationmain]这是该线程的堆栈跟踪:非常感谢任何帮助,以了解可能导致这种崩

  6. ios – 合并子上下文时的NSObjectInaccessbileExceptions

    我尝试手动重现,但失败了.是否有其他可能发生这种情况的情况,是否有处理此类问题的提示?解决方法在创建子上下文时,您可以尝试使用以下行:

  7. ios – 从后台线程调用UIKit时发出警告

    你如何处理项目中的这个问题?

  8. ios – 在SpriteKit中,touchesBegan在与SKScene更新方法相同的线程中运行吗?

    在这里的Apple文档AdvancedSceneProcessing中,它描述了更新方法以及场景的呈现方式,但没有提到何时处理输入.目前尚不清楚它是否与渲染循环位于同一个线程中,或者它是否与它并发.如果我有一个对象,我从SKScene更新方法和touchesBegan方法(在这种情况下是SKSpriteNode)更新,我是否要担心同步对我的对象的两次访问?解决方法所以几天后没有回答我设置了一些实验

  9. ios – 在后台获取中加载UIWebView

    )那么,有一种方法可以在后台加载UIWebView吗?解决方法如果要从用户界面更新元素,则必须在应用程序的主队列(或线程)中访问它们.我建议您在后台继续获取所需的数据,但是当需要更新UIWebView时,请在主线程中进行.你可以这样做:或者您可以创建一个方法来更新UIWebView上的数据,并使用以下方法从后台线程调用它:这将确保您从正确的线程访问UIWebView.希望这可以帮助.

  10. ios – 何时使用Semaphore而不是Dispatch Group?

    我会假设我知道如何使用DispatchGroup,为了解问题,我尝试过:结果–预期–是:为了使用信号量,我实现了:并在viewDidLoad方法中调用它.结果是:从概念上讲,dispachGroup和Semaphore都有同样的目的.老实说,我不熟悉:什么时候使用信号量,尤其是在与dispachGroup合作时–可能–处理问题.我错过了什么部分?

随机推荐

  1. static – 在页面之间共享数据的最佳实践

    我想知道在UWP的页面之间发送像’selectedItem’等变量的最佳做法是什么?创建一个每个页面都知道的静态全局变量类是一个好主意吗?

  2. .net – 为Windows窗体控件提供百分比宽度/高度

    WindowsForm开发的新手,但在Web开发方面经验丰富.有没有办法为Windows窗体控件指定百分比宽度/高度,以便在用户调整窗口大小时扩展/缩小?当窗口调整大小时,可以编写代码来改变控件的宽度/高度,但我希望有更好的方法,比如在HTML/CSS中.在那儿?

  3. 使用Windows Azure查询表存储数据

    我需要使用特定帐户吗?>将应用程序部署到Azure服务后,如何查询数据?GoogleAppEngine有一个数据查看器/查询工具,Azure有类似的东西吗?>您可以看到的sqlExpressintance仅在开发结构中,并且一旦您表示没有等效,所以请小心使用它.>您可以尝试使用Linqpad查询表格.看看JamieThomson的thispost.

  4. windows – SetupDiGetClassDevs是否与文档中的设备实例ID一起使用?

    有没有更好的方法可以使用DBT_DEVICEARRIVAL事件中的数据获取设备的更多信息?您似乎必须指定DIGCF_ALLCLASSES标志以查找与给定设备实例ID匹配的所有类,或者指定ClassGuid并使用DIGCF_DEFAULT标志.这对我有用:带输出:

  5. Windows Live ID是OpenID提供商吗?

    不,WindowsLiveID不是OpenID提供商.他们使用专有协议.自从他们的“测试版”期结束以来,他们从未宣布计划继续它.

  6. 如果我在代码中进行了更改,是否需要重新安装Windows服务?

    我写了一个Windows服务并安装它.现在我对代码进行了一些更改并重新构建了解决方案.我还应该重新安装服务吗?不,只需停止它,替换文件,然后重新启动它.

  7. 带有双引号的字符串回显使用Windows批处理输出文件

    我正在尝试使用Windows批处理文件重写配置文件.我循环遍历文件的行并查找我想要用指定的新行替换的行.我有一个’函数’将行写入文件问题是%Text%是一个嵌入双引号的字符串.然后失败了.可能还有其他角色也会导致失败.如何才能使用配置文件中的所有文本?尝试将所有“在文本中替换为^”.^是转义字符,因此“将被视为常规字符你可以尝试以下方法:其他可能导致错误的字符是:

  8. .net – 将控制台应用程序转换为服务?

    我正在寻找不同的优势/劣势,将我们长期使用的控制台应用程序转换为Windows服务.我们为ActiveMQ使用了一个叫做java服务包装器的东西,我相信人们告诉我你可以用它包装任何东西.这并不是说你应该用它包装任何东西;我们遇到了这个问题.控制台应用程序是一个.NET控制台应用程序,默认情况下会将大量信息记录到控制台,尽管这是可配置的.任何推荐?我们应该在VisualStudio中将其重建为服务吗?我使用“-install”/“-uninstall”开关执行此操作.例如,seehere.

  9. windows – 捕获外部程序的STDOUT和STDERR *同时*它正在执行(Ruby)

    哦,我在Windows上:-(实际上,它比我想象的要简单,这看起来很完美:…是的,它适用于Windows!

  10. windows – 当我试图批量打印变量时,为什么我得到“Echo is on”

    我想要执行一个简单的批处理文件脚本:当我在XP中运行时,它给了我预期的输出,但是当我在Vista或Windows7中运行它时,我在尝试打印值时得到“EchoisOn”.以下是程序的输出:摆脱集合表达式中的空格.等号(=)的两侧可以并且应该没有空格BTW:我通常在@echo关闭的情况下启动所有批处理文件,并以@echo结束它们,所以我可以避免将代码与批处理文件的输出混合.它只是使您的批处理文件输出更好,更清洁.

返回
顶部