如何使用`std :: async`来调用互斥锁保护循环中的函数?

时间:2016-04-28 16:35:55

标签: c++ multithreading c++14

vector<int> vecCustomers;
// populate vecCustomers

void funA()
{
    std::lock_guard<std::mutex> guard( _mutex ); // need lock here
    for(int i=0; i<vecCustomers.size(); ++i)
    {
        funB( vecCustomers[i] ); // can I run this asynchronously       
    }
}

void funB(int i)
{
    // do something here
}

问题&GT; funA访问关键资源,并使用锁来保护资源。 funB不使用任何关键资源,也不需要互斥锁。有没有办法让我可以使用std::async以便我可以调用funB并立即返回以准备循环中调用的下一个funB?此外,在返回函数之前,funB的所有任务都必须完成。

谢谢

==更新==

我根据建议编写以下代码。现在,新问题是为什么所有线程都被第一个线程阻止了?

输出始终如下:

From[0]:H0 << why this thread blocks all others?
From[1]:H1
From[2]:H2
From[3]:H3
From[4]:H4


#include <vector>
#include <future>
#include <mutex>
#include <string>
#include <iostream>
#include <chrono>
using namespace std;

struct ClassA
{
    ClassA()
    {
        vecStr.push_back( "H0" );
        vecStr.push_back( "H1" );
        vecStr.push_back( "H2" );
        vecStr.push_back( "H3" );
        vecStr.push_back( "H4" );
    }

    void start()
    {

        for ( int i = 0; i < 5; ++i )
        {
            std::unique_lock<std::mutex> guard( _mutex );
            std::string strCopy = vecStr[i];

            guard.unlock();
            std::async( std::launch::async, &ClassA::PrintString, this, i, strCopy );
            //PrintString( i, vecStr[i] );
            guard.lock();
        }
    }

    void PrintString( int i, const string& str) const
    {            
        if ( i == 0 )
            std::this_thread::sleep_for( std::chrono::seconds( 10 ) );
        cout << "From[" << i << "]:" << str << endl;
    }

    mutex _mutex;
    vector<string> vecStr;
};

int main()
{
    ClassA ca;

    ca.start();

    return 0;
}

===更新2 ===

#include <vector>
#include <future>
#include <mutex>
#include <string>
#include <iostream>
#include <chrono>
using namespace std;

struct ClassA
{
    ClassA()
    {
        vecStr.push_back( "H0" );
        vecStr.push_back( "H1" );
        vecStr.push_back( "H2" );
        vecStr.push_back( "H3" );
        vecStr.push_back( "H4" );
    }

    void start()
    {
        std::vector<std::future<void>> result;
        for ( int i = 0; i < 5; ++i )
        {
            std::unique_lock<std::mutex> guard( _mutex );
            std::string strCopy = vecStr[i];

            guard.unlock();
            result.push_back( std::async( std::launch::async, &ClassA::PrintString, this, i, strCopy ) );
            //PrintString( i, vecStr[i] );
        }

        for(auto &e : result) 
        {
            e.get();
        }
    }

    void PrintString( int i, const string& str) const
    {            
        static std::mutex m;
        std::unique_lock<std::mutex> _(m);

        if ( i == 0 )
        {
            cout << "From[" << i << "]:" << str << " sleep for a while" << endl;
            _.unlock();
            std::this_thread::sleep_for( std::chrono::seconds( 10 ) );
        }
        else
            cout << "From[" << i << "]:" << str << endl;
    }

    mutex _mutex;
    vector<string> vecStr;
};

int main()
{
    ClassA ca;

    ca.start();

    return 0;
}

2 个答案:

答案 0 :(得分:4)

你看到按顺序执行调用的主要原因是你没有以任何方式利用并行性(等等,什么?但是......)。让我解释一下

std::async并不只是启动要异步运行的任务,它还会返回一个std::future,可用于获取返回的值(如果启动的函数返回某些内容)。但是,由于您未存储未来,因此在启动任务后会立即销毁它。不幸的是,在这种情况下,析构函数会阻塞,直到调用完成。

  如果满足以下所有条件,则[pd :: future ::〜future()]可能会阻塞:共享状态是通过调用std :: async创建的,共享状态尚未就绪,这是最后一个对共享状态的引用。

quote)很多人因为这个事实而表示沮丧,但这是由标准设定的。

所以你要做的就是存储std::future s(在向量或其他东西中),直到所有的都被启动。

答案 1 :(得分:0)

您可以在保持互斥锁的同时调用std::async。缺点是调用std::async需要一些时间,从而增加了锁定互斥锁的时间,从而降低了并行性。

在持有锁的同时制作该矢量的副本可能更便宜。然后释放互斥锁并异步处理副本。

您的优化目标是最小化互斥锁被锁定的时间以及可能花费在函数上的时间。