Монитор от Герба Саттера

В недавно вышедшем выступлении Герба Саттера посвященном многопоточности он привел интересный пример примитива для синхронного выполнения последовательностей операций. Сам Майерс окрестил детище “монитором”, по аналогии с мониторами из мира Java и C#, хотя на мой взгляд, сходства между ними не так уж и много. Суть задумки в том, что бы синхронизировать работу с каким-либо объектом, который изначально не поддерживает синхронизации. При этом, обеспечив синхронность не только на уровне одной операции, но и на уровне “трансзакции”.

string s = "Start\n";
vector<future<void>> v;

for(int i=0; i<5; ++i)
{
    v.push_back(async([&,i]{
        {
            // необходимо сделать атомарно.
            s += to_string(i) + " " + to_string(i);
            s += "\n";
        }
        {
            // так же необходимо сделать атомарно.
            cout << s;
        }
    }));
}

В принципе, в приведенном выше примере вполне можно воспользоваться комбинацией mutex + lock_guard, но так не интересно, не красиво, да и о чем было бы рассказывать в течении полутора часов?
Решение предложенно действительно элегантное:

template<typename T>
class Monitor
{
public:
    mutable T t_;
    mutable mutex m_;
public:
    Monitor(T t = T{}) : t_(t) {}

    template<typename F>
    auto operator()(F f) const -> decltype(f(t_))
    {
        lock_guard<mutex>_{m_};
        return f(t_);
    }
};

Что позволяет упростить запись до следущей:

string s = "Start\n";
Monitor<string&> m{s};
vector<future<void>> v;

for(int i=0; i<5; ++i)
{
    v.push_back(async([&,i]{
        m([=](string &s) {
            s += to_string(i) + " " + to_string(i);
            s += "\n";
        });
        m([](string &s){
            cout << s;
        });
    }));
}

Не правда ли элегантное решение? Разве что его обоснование сочетанию mutable + const в operator() смущают.

1 Comment Монитор от Герба Саттера

Leave a Reply