خرید بک لینک

Vote count: 0

Tim Harris said:

https://en.wikipedia.org/wiki/Software_transactional_memory#Composable_operations

Perhaps the most fundamental objection [...] is that lock-based programs do not compose: correct fragments may fail when combined. For example, consider a hash table with thread-safe insert and delete operations. Now suppose that we want to delete one item A from table t1, and insert it into table t2; but the intermediate state (in which neither table contains the item) must not be visible to other threads. Unless the implementor of the hash table anticipates this need, there is simply no way to satisfy this requirement. [...] In short, operations that are individually correct (insert, delete) caot be composed into larger correct operations. —Tim Harris et al., "Composable Memory Transactions", Section 2: Background, pg.2[6]

What does it mean?

If I have 2 hash maps std::unordered_map and 2 mutexes std::mutex (one for each hash map) then I can simply lock its both: http://ideone.com/6RSNyN

#include <iostream>
#include <string>
#include <mutex>
#include <thread>
#include <chrono>
#include <unordered_map>

std::unordered_map<std::string, std::string> map1 ( {{"apple","red"},{"lemon","yellow"}} );
std::mutex mtx1;

std::unordered_map<std::string, std::string> map2 ( {{"orange","orange"},{"strawberry","red"}} );
std::mutex mtx2;

void func() {
    std::lock_guard<std::mutex> lock1(mtx1);
    std::lock_guard<std::mutex> lock2(mtx2);

    std::cout << "map1: ";
    for (auto& x: map1) std::cout << " " << x.first << " => " << x.second << ", ";
    std::cout << std::endl << "map2: ";
    for (auto& x: map2) std::cout << " " << x.first << " => " << x.second << ", ";
    std::cout << std::endl << std::endl;

    auto it1 = map1.find("apple");
    if(it1 != map1.end()) {
        auto val = *it1;
        map1.erase(it1);
        std::this_thread::sleep_for(std::chrono::duration<double, std::milli>(1000));
        map2[val.first] = val.second;
    }
}

int main ()
{
    std::thread t1(func);
    std::this_thread::sleep_for(std::chrono::duration<double, std::milli>(500));
    std::thread t2(func);
    t1.join();
    t2.join();

    retu 0;
}

If I want to implement myself thread-safe hash map my_unordered_map then I will implement such thing:

template<typename key, template val>
class my_unordered_map {
    std::recursive_mutex mtx_ptr;
    void lock() { mtx_ptr->lock(); }
    void unlock() { mtx_ptr->unlock(); }
    template<typename mutex_type> friend class std::lock_guard;
public:
 // .. all required public methods which lock recursive mutex before do anything
};

And will use it such:

my_unordered_map<std::string, std::string> map1 ( {{"apple","red"},{"lemon","yellow"}} );

my_unordered_map<std::string, std::string> map2 ( {{"orange","orange"},{"strawberry","red"}} );

void func() {
    std::lock_guard<my_unordered_map> lock1(map1);
    std::lock_guard<my_unordered_map> lock2(map2);

    // work with map1 and map2
    // recursive_mutex allow multiple locks in: lock1(map1) and map1->at(key)
}

Similar I get thread-safe code and fully sequential consistency for both map1 and map2.

But did about which cases this is said?

Perhaps the most fundamental objection [...] is that lock-based programs do not compose: correct fragments may fail when combined.

asked 33 secs ago

برچسب: نویسنده: استخدام کار تاريخ: يکشنبه 13 تير 1395 ساعت: 4:29

صفحه بندی