Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

Chapter 81: Thread synchronization structures

Working with threads might need some synchronization techniques if the threads interact. In this topic, you can find the di erent structures which are provided by the standard library to solve these issues.

Section 81.1: std::condition_variable_any, std::cv_status

A generalization of std::condition_variable, std::condition_variable_any works with any type of BasicLockable structure.

std::cv_status as a return status for a condition variable has two possible return codes:

std::cv_status::no_timeout: There was no timeout, condition variable was notified std::cv_status::no_timeout: Condition variable timed out

Section 81.2: std::shared_lock

A shared_lock can be used in conjunction with a unique lock to allow multiple readers and exclusive writers.

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

class PhoneBook { public:

string getPhoneNo( const std::string & name )

{

shared_lock<shared_timed_mutex> r(_protect); auto it = _phonebook.find( name );

if ( it == _phonebook.end() ) return (*it).second;

return "";

}

void addPhoneNo ( const std::string & name, const std::string & phone )

{

unique_lock<shared_timed_mutex> w(_protect); _phonebook[name] = phone;

}

shared_timed_mutex _protect; unordered_map<string,string> _phonebook;

};

Section 81.3: std::call_once, std::once_flag

std::call_once ensures execution of a function exactly once by competing threads. It throws std::system_error in case it cannot complete its task.

Used in conjunction with std::once_flag.

#include <mutex>

GoalKicker.com – C++ Notes for Professionals

443

#include <iostream>

std::once_flag flag; void do_something(){

std::call_once(flag, [](){std::cout << "Happens once" << std::endl;});

std::cout << "Happens every time" << std::endl;

}

Section 81.4: Object locking for e cient access

Often you want to lock the entire object while you perform multiple operations on it. For example, if you need to examine or modify the object using iterators. Whenever you need to call multiple member functions it is generally more e cient to lock the whole object rather than individual member functions.

For example:

class text_buffer

{

// for readability/maintainability

using mutex_type = std::shared_timed_mutex;

using reading_lock = std::shared_lock<mutex_type>; using updates_lock = std::unique_lock<mutex_type>;

public:

//This returns a scoped lock that can be shared by multiple

//readers at the same time while excluding any writers

[[nodiscard]]

reading_lock lock_for_reading() const { return reading_lock(mtx); }

//This returns a scoped lock that is exclusing to one

//writer preventing any readers

[[nodiscard]]

updates_lock lock_for_updates() { return updates_lock(mtx); }

char* data() { return buf; }

char const* data() const { return buf; }

char* begin() { return buf; }

char const* begin() const { return buf; }

char* end() { return buf + sizeof(buf); }

char const* end() const { return buf + sizeof(buf); }

std::size_t size() const { return sizeof(buf); }

private:

char buf[1024];

mutable mutex_type mtx; // mutable allows const objects to be locked

};

When calculating a checksum the object is locked for reading, allowing other threads that want to read from the object at the same time to do so.

std::size_t checksum(text_buffer const& buf)

{

std::size_t sum = 0xA44944A4; // lock the object for reading

GoalKicker.com – C++ Notes for Professionals

444

auto lock = buf.lock_for_reading();

for(auto c: buf)

sum = (sum << 8) | (((unsigned char) ((sum & 0xFF000000) >> 24)) ^ c);

return sum;

}

Clearing the object updates its internal data so it must be done using an exclusing lock.

void clear(text_buffer& buf)

{

auto lock = buf.lock_for_updates(); // exclusive lock std::fill(std::begin(buf), std::end(buf), '\0');

}

When obtaining more than one lock care should be taken to always acquire the locks in the same order for all threads.

void transfer(text_buffer const& input, text_buffer& output)

{

auto lock1 = input.lock_for_reading(); auto lock2 = output.lock_for_updates();

std::copy(std::begin(input), std::end(input), std::begin(output));

}

note: This is best done using std::deferred::lock and calling std::lock

GoalKicker.com – C++ Notes for Professionals

445

Chapter 82: The Rule of Three, Five, And

Zero

Section 82.1: Rule of Zero

Version ≥ C++11

We can combine the principles of the Rule of Five and RAII to get a much leaner interface: the Rule of Zero: any resource that needs to be managed should be in its own type. That type would have to follow the Rule of Five, but all users of that resource do not need to write any of the five special member functions and can simply default all of them.

Using the Person class introduced in the Rule of Three example, we can create a resource-managing object for cstrings:

class cstring { private:

char* p;

public:

~cstring() { delete [] p; } cstring(cstring const& ); cstring(cstring&& );

cstring& operator=(cstring const& ); cstring& operator=(cstring&& );

/* other members as appropriate */

};

And once this is separate, our Person class becomes far simpler:

class Person { cstring name; int arg;

public:

~Person() = default; Person(Person const& ) = default; Person(Person&& ) = default;

Person& operator=(Person const& ) = default; Person& operator=(Person&& ) = default;

/* other members as appropriate */

};

The special members in Person do not even need to be declared explicitly; the compiler will default or delete them appropriately, based on the contents of Person. Therefore, the following is also an example of the rule of zero.

struct Person { cstring name; int arg;

};

If cstring were to be a move-only type, with a deleted copy constructor/assignment operator, then Person would automatically be move-only as well.

GoalKicker.com – C++ Notes for Professionals

446