繁体   English   中英

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

[英]How to use `std::async` to call a function in a mutex protected loop?

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
}

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

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

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

如果满足以下所有条件,则[std :: future ::〜future()]可能会阻塞:共享状态是通过调用std :: async创建的,该共享状态尚未准备好,这是最后一个参考到共享状态。

报价 )很多人都表示无奈,由于这个事实,这就是它是如何通过标准的设置。

因此,您要做的就是存储std::future (以向量或其他方式),直到全部启动。

您当然可以在保持互斥锁的同时调用std::async 缺点是调用std::async会花费一些时间,从而增加了锁定该互斥锁的时间,从而降低了并行度。

按住锁时复制该向量可能会更便宜。 然后释放互斥锁并异步处理副本。

您的优化目标是最大程度地减少互斥锁被锁定的时间,以及可能花费在该功能上的时间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM