๐Ÿ˜‡Lock V1

C++ API

ไบ’ๆ–ฅ้”๏ผˆMutex๏ผ‰

ไธบไบ†้ฟๅ…ๅคšไธช็บฟ็จ‹ๅœจๆŸไธ€ๆ—ถๅˆปๅŒๆ—ถๆ“ไฝœไธ€ไธชๅ…ฑไบซ่ต„ๆบ

ไพ‹ๅฆ‚็บฟ็จ‹ๆฑ ไธญ็š„ๆœ‰ๅคšไธช็ฉบ้—ฒ็บฟ็จ‹ๅ’Œไธ€ไธชไปปๅŠก้˜Ÿๅˆ—, ไปปไฝ•ไธ€ไธช็บฟ็จ‹้ƒฝ่ฆไฝฟ็”จไบ’ๆ–ฅ้”ไบ’ๆ–ฅ่ฎฟ้—ฎไปปๅŠก้˜Ÿๅˆ—๏ผŒไปฅ้ฟๅ…ๅคšไธช็บฟ็จ‹ๅŒๆ—ถ่ฎฟ้—ฎไปปๅŠก้˜Ÿๅˆ—ไปฅๅ‘็”Ÿ้”™ไนฑใ€‚

ๅœจๆŸไธ€ๆ—ถๅˆป๏ผŒๅชๆœ‰ไธ€ไธช็บฟ็จ‹ๅฏไปฅ่Žทๅ–ไบ’ๆ–ฅ้”๏ผŒๅœจ้‡Šๆ”พไบ’ๆ–ฅ้”ไน‹ๅ‰ๅ…ถไป–็บฟ็จ‹้ƒฝไธ่ƒฝ่Žทๅ–่ฏฅไบ’ๆ–ฅ้”ใ€‚ๅฆ‚ๆžœๅ…ถไป–็บฟ็จ‹ๆƒณ่ฆ่Žทๅ–่ฟ™ไธชไบ’ๆ–ฅ้”๏ผŒ้‚ฃไนˆ่ฟ™ไธช็บฟ็จ‹ๅช่ƒฝไปฅ้˜ปๅกžๆ–นๅผ่ฟ›่กŒ็ญ‰ๅพ…ใ€‚

ๆž„้€ std::mutex็š„ๅฎžไพ‹ๅˆ›ๅปบไบ’ๆ–ฅๅ…ƒ๏ผŒ่ฐƒ็”จๆˆๅ‘˜ๅ‡ฝๆ•ฐlock()ๆฅ้”ๅฎšๅฎƒ๏ผŒ่ฐƒ็”จunlock()ๆฅ่งฃ้”

ไธ่ฟ‡ไธ€่ˆฌไธๆŽจ่่ฟ™็งๅšๆณ•๏ผŒๆ ‡ๅ‡†C++ๅบ“ๆไพ›ไบ†std::lock_guard็ฑปๆจกๆฟ๏ผŒๅฎž็Žฐไบ†ไบ’ๆ–ฅๅ…ƒ็š„RAIIๆƒฏ็”จ่ฏญๆณ•ใ€‚std::mutexๅ’Œstd::lock _ guard

#include <mutex>
#include <list> // ไบ’ๆ–ฅๅ…ƒไฟๆŠคๅˆ—่กจ

std::list<int> this_list;
std::mutex this_mutex;

void add_to_list (int value) {
    std::lock_guard<std::mutex> guard(this_mutex);
    this_list.push_back(value);
}

่ฟ™้‡Œๆณจๆ„ๆญป้”๏ผšๅคšไธช็บฟ็จ‹ไบ‰ๅคบๅ…ฑไบซ่ต„ๆบๅฏผ่‡ดๆฏไธช็บฟ็จ‹้ƒฝไธ่ƒฝๅ–ๅพ—่‡ชๅทฑๆ‰€้œ€็š„ๅ…จ้ƒจ่ต„ๆบ๏ผŒไปŽ่€Œ็จ‹ๅบๆ— ๆณ•ๅ‘ไธ‹ๆ‰ง่กŒ

  • ไบ’ๆ–ฅ๏ผˆ่ต„ๆบๅŒไธ€ๆ—ถๅˆปๅช่ƒฝ่ขซไธ€ไธช่ฟ›็จ‹ไฝฟ็”จ๏ผ‰

  • ่ฏทๆฑ‚ๅนถไฟๆŒ๏ผˆ่ฟ›็จ‹ๅœจ่ฏท่ต„ๆบๆ—ถ๏ผŒไธ้‡Šๆ”พ่‡ชๅทฑๅทฒ็ปๅ ๆœ‰็š„่ต„ๆบ๏ผ‰

  • ไธๅ‰ฅๅคบ๏ผˆ่ฟ›็จ‹ๅทฒ็ป่Žทๅพ—็š„่ต„ๆบ๏ผŒๅœจ่ฟ›็จ‹ไฝฟ็”จๅฎŒๅ‰๏ผŒไธ่ƒฝๅผบๅˆถๅ‰ฅๅคบ๏ผ‰

  • ๅพช็Žฏ็ญ‰ๅพ…๏ผˆ่ฟ›็จ‹้—ดๅฝขๆˆ็Žฏ็Šถ็š„่ต„ๆบๅพช็Žฏ็ญ‰ๅพ…ๅ…ณ็ณป๏ผ‰

็›ดๆŽฅๆ“ไฝœ mutex๏ผŒๅณ็›ดๆŽฅ่ฐƒ็”จ mutex ็š„ lock / unlock ๅ‡ฝๆ•ฐ

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex mutex_1;
int count_1 = 0;

void counter() {
    mutex_1.lock();
    
    int i = ++count_1;
    โ€ฆโ€ฆ
    
    mutex_1.unlock();
}

int main() {
    const std::size_t SIZE = 4;
    // create a group of threads
    std::vector<std::thread> v;
    v.reverse(SIZE);
    for (std::size_t i = 0; i < SIZE; ++i) {
        v.emplace_back(&counter);
    }
    
    // waiting for the end of all threads
    for (std::thread& t : v) {
        t.join();
    }
    return 0;
}

lock_guard

ไฝฟ็”จ lock_guard ่‡ชๅŠจๅŠ ้”ใ€่งฃ้”ใ€‚ๅŽŸ็†ๆ˜ฏ RAII๏ผŒๅ’Œๆ™บ่ƒฝๆŒ‡้’ˆ็ฑปไผผใ€‚

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex mutex_2;
int count_2 = 0;

void counter() {
    // lock_guard ๅœจๆž„้€ ๅ‡ฝๆ•ฐ้‡ŒๅŠ ้”๏ผŒๅœจๆžๆž„ๅ‡ฝๆ•ฐ้‡Œ่งฃ้”ใ€‚
    std::lock_guard<std::mutex> lock(mutex_2);
    int i = ++count_2;
    โ€ฆโ€ฆ
}

int main() {
    const std::size_t SIZE = 4;
    std::vector<std::thread> v;
    v.reverse(SIZE);
    for (std::size_t i = 0; i < SIZE; ++i) {
        v.emplace_back(&counter);
    }
    
    // waiting for the end of all threads
    for (std::thread& t : v) {
        t.join();
    }
    return 0;
}
}

unique_lock

ไฝฟ็”จ unique_lock ่‡ชๅŠจๅŠ ้”ใ€่งฃ้”ใ€‚ unique_lock ไธŽ lock_guard ๅŽŸ็†็›ธๅŒ๏ผŒไฝ†ๆ˜ฏๆไพ›ไบ†ๆ›ดๅคšๅŠŸ่ƒฝ๏ผˆๆฏ”ๅฆ‚ๅฏไปฅ็ป“ๅˆๆกไปถๅ˜้‡ไฝฟ็”จ๏ผ‰ใ€‚ ๆณจๆ„๏ผšmutex::scoped_lock ๅ…ถๅฎžๅฐฑๆ˜ฏ unique_lock ็š„ typedef!

counter ๅ‡ฝๆ•ฐไฝ“๏ผš

void counter() {
    std::unique_lock<std::mutex> lock(mutex_3);
    int i = ++count_3;
    โ€ฆโ€ฆ
}

std::recursive_mutex

ๅฐฑๅƒไบ’ๆ–ฅ้”๏ผˆmutex๏ผ‰ไธ€ๆ ท๏ผŒ้€’ๅฝ’ไบ’ๆ–ฅ้”๏ผˆrecursive_mutex๏ผ‰ๆ˜ฏๅฏ้”ๅฎš็š„ๅฏน่ฑก๏ผŒไฝ†ๅฎƒๅ…่ฎธๅŒไธ€็บฟ็จ‹่Žทๅพ—ๅฏนไบ’ๆ–ฅ้”ๅฏน่ฑก็š„ๅคš็บงๆ‰€ๆœ‰ๆƒ๏ผˆๅคšๆฌกlock๏ผ‰ใ€‚

่ฟ™ๅ…่ฎธไปŽๅทฒ็ป้”ๅฎšๅฎƒ็š„็บฟ็จ‹้”ๅฎš๏ผˆๆˆ–ๅฐ่ฏ•้”ๅฎš๏ผ‰ไบ’ๆ–ฅๅฏน่ฑก๏ผŒไปŽ่€Œ่Žทๅพ—ๅฏนไบ’ๆ–ฅๅฏน่ฑก็š„ๆ–ฐๆ‰€ๆœ‰ๆƒ็บงๅˆซ๏ผš

ไบ’ๆ–ฅๅฏน่ฑกๅฎž้™…ไธŠๅฐ†ไฟๆŒๅฏน่ฏฅ็บฟ็จ‹็š„้”ๅฎš๏ผŒ็›ดๅˆฐ่ฐƒ็”จๅ…ถๆˆๅ‘˜ unlock ็š„ๆฌกๆ•ฐไธŽๆญคๆ‰€ๆœ‰ๆƒ็บงๅˆซ็š„ๆฌกๆ•ฐ็›ธๅŒใ€‚

  1. ่ฐƒ็”จ็บฟ็จ‹ไปŽๆˆๅŠŸ่ฐƒ็”จ lock ๆˆ– try_lock ๅผ€ๅง‹ๅ ๆœ‰recursive_mutex๏ผŒ ๆœŸ้—ด็บฟ็จ‹ๅฏไปฅ่ฟ›่กŒๅฏน lock ๆˆ– try_lock็š„้™„ๅŠ ่ฐƒ็”จ๏ผŒๆ‰€ๆœ‰ๆƒๅœจ็บฟ็จ‹่ฐƒ็”จ unlock ๅŒน้…ๆฌกๆ•ฐๆ—ถ็ป“ๆŸใ€‚

  2. ็บฟ็จ‹ๅ ๆœ‰recursive_mutexๆ—ถ๏ผŒ่‹ฅๅ…ถไป–็บฟ็จ‹่ฆๆฑ‚recursive_mutexๆ‰€ๆœ‰ๆƒ๏ผŒ่ฐƒ็”จlockๅฐ†่ขซ้˜ปๅกž๏ผŒ่ฐƒ็”จtry_lockๅฐ†่ฟ”ๅ›žfalse.

  3. ๅฏ้”ๅฎšrecursive_mutex็š„ๆœ€ๅคงๆฌกๆ•ฐๆœชๆŒ‡ๅฎš็š„๏ผŒไฝ†ๅˆฐ่พพ่ฏฅๆ•ฐๅŽ๏ผŒๅฏน lock ็š„่ฐƒ็”จๅฐ†ๆŠ›ๅ‡บ std::system_error ่€Œๅฏน try_lock ็š„่ฐƒ็”จ่ฟ”ๅ›žfalse;

  4. ่‹ฅrecursive_mutexๅœจไป่ขซ็บฟ็จ‹ๅ ๆœ‰ๆ—ถ่ขซ้”€ๆฏ๏ผŒๅˆ™็จ‹ๅบ่กŒไธบๆœชๅฎšไน‰ใ€‚recursive_mutexๆปก่ถณ mutex ๅ’Œ ๆ ‡ๅ‡†ๅธƒๅฑ€็ฑปๅž‹็š„ๆ‰€ๆœ‰่ฆๆฑ‚ใ€‚

#include <iostream>
#include <thread>
#include <mutex> 

std::recursive_mutex mtx;           

void print_block (int n, char c) {
  mtx.lock();
  mtx.lock();
  mtx.lock();
  
  for (int i=0; i<n; ++i) { 
      std::cout << c; 
  }
  std::cout << '\n';
  
  mtx.unlock();
  mtx.unlock();
  mtx.unlock();
}

int main () {
  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

std::timed_mutex

ๅฎšๆ—ถไบ’ๆ–ฅ้”ๆ˜ฏไธ€ไธชๅฏๆ—ถ้—ด้”ๅฎš็š„ๅฏน่ฑก๏ผŒๆ—จๅœจ้€š็Ÿฅไฝ•ๆ—ถๅ…ณ้”ฎไปฃ็ ้œ€่ฆ็‹ฌๅ ่ฎฟ้—ฎ๏ผŒๅฐฑๅƒๅธธ่ง„ไบ’ๆ–ฅ้”ไธ€ๆ ท๏ผŒไฝ†่ฟ˜ๆ”ฏๆŒๅฎšๆ—ถๅฐ่ฏ•้”ๅฎš่ฏทๆฑ‚ใ€‚

lock่ฐƒ็”จ็บฟ็จ‹ๅฐ†้”ๅฎštimed_mutex๏ผŒๅนถๅœจๅฟ…่ฆๆ—ถ่ฟ›่กŒ้˜ปๅกž๏ผˆๅ…ถ่กŒไธบไธŽ mutex ๅฎŒๅ…จ็›ธๅŒ๏ผ‰

try_lock

่ฐƒ็”จ็บฟ็จ‹ๅฐ†้”ๅฎštimed_mutex๏ผŒๅนถๅœจๅฟ…่ฆๆ—ถ่ฟ›่กŒ้˜ปๅกž๏ผˆๅ…ถ่กŒไธบไธŽ mutex ๅฎŒๅ…จ็›ธๅŒ๏ผ‰

try_lock_for

ๅฐ่ฏ•้”ๅฎš timed_mutex๏ผŒ ๆœ€ๅคš้˜ปๅกž rel_time ๆ—ถ้—ด

try_lock_until

ๅฐ่ฏ•้”ๅฎš timed_mutex๏ผŒๆœ€ๅคš้˜ปๅกžๅˆฐ abs_time ๆ—ถ้—ด็‚น

unlock

่งฃ้” timed_mutex๏ผŒ้‡Šๆ”พๅฏนๅ…ถ็š„ๆ‰€ๆœ‰ๆƒ๏ผˆๅ…ถ่กŒไธบไธŽ mutex ็›ธๅŒ๏ผ‰

std::recursive_timed_mutex

้€’ๅฝ’ๅฎšๆ—ถไบ’ๆ–ฅ้”ๅฐ† recursive_timed ๅ’Œ timed_mutex ็š„ๅŠŸ่ƒฝ็ป“ๅˆๅˆฐไธ€ไธช็ฑปไธญ๏ผš

  • ๅฎƒๆ—ขๆ”ฏๆŒ้€š่ฟ‡ๅ•ไธช็บฟ็จ‹่Žทๅ–ๅคšไธช้”ๅฎš็บงๅˆซ

  • ๅˆๆ”ฏๆŒๅฎšๆ—ถ็š„ try_lock ่ฏทๆฑ‚ใ€‚

ๆˆๅ‘˜ๅ‡ฝๆ•ฐไธŽ timed_mutex ็›ธๅŒใ€‚

once_flagใ€call_onceไฝฟ็”จ

ๅœจๅคš็บฟ็จ‹ไธญ๏ผŒๆœ‰ไธ€็งๅœบๆ™ฏๆ˜ฏๆŸไธชไปปๅŠกๅช้œ€่ฆๆ‰ง่กŒไธ€ๆฌก๏ผŒๅฏไปฅ็”จC++11ไธญ็š„std::call_onceๅ‡ฝๆ•ฐ้…ๅˆstd::once_flagๆฅๅฎž็Žฐใ€‚

ๅคšไธช็บฟ็จ‹ๅŒๆ—ถ่ฐƒ็”จๆŸไธชๅ‡ฝๆ•ฐ๏ผŒstd::call_onceๅฏไปฅไฟ่ฏๅคšไธช็บฟ็จ‹ๅฏน่ฏฅๅ‡ฝๆ•ฐๅช่ฐƒ็”จไธ€ๆฌก

ๅฎž็Žฐ็บฟ็จ‹ๅฎ‰ๅ…จ็š„ๅ•ไพ‹ๆจกๅผ

// hๆ–‡ไปถ
#pragma once
#include <thread>
#include <iostream>
#include <mutex>
#include <memory>

class Task {
private:
	Task();
public:
	static Task* task;
	static Task* getInstance();
	void fun();
};
// cppๆ–‡ไปถ
Task* Task::task;
Task::Task() {
	std::cout << "ๆž„้€ ๅ‡ฝๆ•ฐ" << std::endl;
}

Task* Task::getInstance() {
	static std::once_flag flag;
	std::call_once(flag, []	{
		task = new Task();
	});
	return task;
}

void Task::fun() {
	std::cout << "hello world!"<< std::endl;
}

ๆกไปถ้”

ๆกไปถ้”ๅฐฑๆ˜ฏๆ‰€่ฐ“็š„ๆกไปถๅ˜้‡, ไธๆ˜ฏ็”จๆฅ็ฎก็†ไบ’ๆ–ฅ้‡็š„๏ผŒๅฎƒ็š„ไฝœ็”จๆ˜ฏ็”จๆฅๅŒๆญฅ็บฟ็จ‹๏ผŒๅฎƒ็š„็”จๆณ•็›ธๅฝ“ไบŽ็ผ–็จ‹ไธญๅธธ่ง็š„flagๆ ‡ๅฟ—๏ผˆAใ€Bไธคไธชไบบ็บฆๅฎšflag=trueไธบ่กŒๅŠจๅท่ง’๏ผŒ้ป˜่ฎคflagไธบfalse,Aไธๆ–ญ็š„ๆฃ€ๆŸฅflag็š„ๅ€ผ,ๅช่ฆBๅฐ†flagไฟฎๆ”นไธบtrue๏ผŒAๅฐฑๅผ€ๅง‹่กŒๅŠจ๏ผ‰

ๆŸไธ€ไธช็บฟ็จ‹ๅ› ไธบๆŸไธชๆกไปถๆœชๆปก่ถณๆ—ถ๏ผŒๅฏไปฅไฝฟ็”จๆกไปถๅ˜้‡ไฝฟๆ”น็จ‹ๅบๅค„ไบŽ้˜ปๅกž็Šถๆ€ใ€‚

ไธ€ๆ—ฆๆกไปถๆปก่ถณ๏ผŒๅˆ™ไปฅโ€œไฟกๅท้‡โ€็š„ๆ–นๅผๅ”ค้†’ไธ€ไธชๅ› ไธบ่ฏฅๆกไปถ่€Œ่ขซ้˜ปๅกž็š„็บฟ็จ‹ใ€‚

ๆœ€ไธบๅธธ่งๅฐฑๆ˜ฏๅœจ็บฟ็จ‹ๆฑ ไธญ๏ผŒ่ตทๅˆๆฒกๆœ‰ไปปๅŠกๆ—ถไปปๅŠก้˜Ÿๅˆ—ไธบ็ฉบ๏ผŒๆญคๆ—ถ็บฟ็จ‹ๆฑ ไธญ็š„็บฟ็จ‹ๅ› ไธบโ€œไปปๅŠก้˜Ÿๅˆ—ไธบ็ฉบโ€่ฟ™ไธชๆกไปถๅค„ไบŽ้˜ปๅกž็Šถๆ€ใ€‚ไธ€ๆ—ฆๆœ‰ไปปๅŠก่ฟ›ๆฅ๏ผŒๅฐฑไผšไปฅไฟกๅท้‡็š„ๆ–นๅผๅ”ค้†’ไธ€ไธช็บฟ็จ‹ๆฅๅค„็†่ฟ™ไธชไปปๅŠกใ€‚

็ฑปๅž‹๏ผš

  • std::condition_variable๏ผˆๅชๅ’Œstd::mutexไธ€่ตทๅทฅไฝœ๏ผ‰

  • std::condition_variable_any๏ผˆ็ฌฆๅˆ็ฑปไผผไบ’ๆ–ฅๅ…ƒ็š„ๆœ€ไฝŽๆ ‡ๅ‡†็š„ไปปไฝ•ไธœ่ฅฟไธ€่ตทๅทฅไฝœ๏ผ‰

// std::condition_variable waiting for data
#include <condition_variable>
#include <mutex>
#include <queue>
โ€ฆโ€ฆ

std::mutex mut;
std::queue<data_chunck> data_queue;
std::condition_variable data_con;

void data_preparing_thread () {
    while (more_data_to_prepare()) {
        data_chunck const data = prepare_data();
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(data);
        data_con.notify_one();
    }
}

void data_processing_thread () {
    while (true) {
        std::unique_lock<std::mutex> lk(mut); //่ฟ™้‡Œไฝฟ็”จunique_lockๆ˜ฏไธบไบ†ๅŽ้ขๆ–นไพฟ่งฃ้”
        data.con.wait(lk, {
            []return !data_queue.empty();
        });
        data_chunck data = data_queue.front();
        data_queue.pop();
        lk.unlock();
        process(data);
        if (is_last_chunck(data)) {
            break;
        }
    }
}

eg 2:

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <chrono>

std::deque<int> q;
std::mutex mu;
std::condition_variable condi;

void function_1() {
	int count = 10;
	while (count > 0) {
		std::unique_lock<std::mutex> locker(mu);
		q.push_back(count);
		locker.unlock();
		condi.notify_one();			// ้€š็Ÿฅไธ€ไธช็ญ‰ๅพ…็บฟ็จ‹ๆฟ€ๆดป   condi.notify_all()ๆฟ€ๆดปๆ‰€ๆœ‰็บฟ็จ‹
		count--;
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

void function_2() {
	int data = 100;
	while (data > 1) {
		std::unique_lock<std::mutex> locker(mu);
		condi.wait(locker,			// ่งฃ้”locker,ๅนถ่ฟ›ๅ…ฅไผ‘็œ   ๆ”ถๅˆฐnotifyๆ—ถๅˆ้‡ๆ–ฐๅŠ ้”
			[]() { return !q.empty(); });   // ๅฆ‚ๆžœqไธไธบ็ฉบ ็บฟ็จ‹ๆ‰ไผš่ขซๆฟ€ๆดป
		data = q.front();
		q.pop_front();
		locker.unlock();

		std::cout << data << std::endl;
	}
}
int main() {
	std::thread t1(function_1);
	std::thread t2(function_2);

	t1.join();
	t2.join();
	
	return 0;
}

cond.notify_one(): ้šๆœบๅ”ค้†’ไธ€ไธช็ญ‰ๅพ…็š„็บฟ็จ‹

cond.notify_all(): ๅ”ค้†’ๆ‰€ๆœ‰็ญ‰ๅพ…็š„็บฟ็จ‹

wait()็š„ๅฎž็Žฐ: ๆฃ€ๆŸฅๆกไปถ๏ผŒๅนถๅœจๆปก่ถณๆ—ถ่ฟ”ๅ›žใ€‚

ไธคไธช้‡่ฝฝ๏ผš

void wait( std::unique_lock<std::mutex>& lock );                  //  (1)	(since C++11)

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );  //  (2)	(since C++11)

ๅฆ‚ๆžœๆกไปถไธๆปก่ถณ๏ผŒwait()่งฃ้”ไบ’ๆ–ฅๅ…ƒ๏ผŒๅนถๅฐ†่ฏฅ็บฟ็จ‹็ฝฎไบŽ้˜ปๅกžๆˆ–็ญ‰ๅพ…็Šถๆ€ใ€‚

ๅฝ“ๆฅ่‡ชๆ•ฐๆฎๅ‡†ๅค‡็บฟ็จ‹ไธญๅฏนnotify_one()็š„่ฐƒ็”จ้€š็Ÿฅๆกไปถๅ˜้‡ๆ—ถ๏ผŒ็บฟ็จ‹ไปŽ็ก็œ ็Šถๆ€ไธญ่‹้†’๏ผˆ่งฃ้™คๅ…ถ้˜ปๅกž๏ผ‰๏ผŒ้‡ๆ–ฐ่Žทๅพ—ไบ’ๆ–ฅๅ…ƒไธŠ็š„้”๏ผŒๅนถๅ†ๆฌกๆฃ€ๆŸฅๆกไปถ๏ผŒๅฆ‚ๆžœๆกไปถๅทฒ็ปๆปก่ถณ๏ผŒๅฐฑไปŽwait()่ฟ”ๅ›žๅ€ผ๏ผŒไบ’ๆ–ฅๅ…ƒไป่ขซ้”ๅฎšใ€‚ๅฆ‚ๆžœๆกไปถไธๆปก่ถณ๏ผŒ่ฏฅ็บฟ็จ‹่งฃ้”ไบ’ๆ–ฅๅ…ƒ๏ผŒๅนถๆขๅค็ญ‰ๅพ…ใ€‚

  • void wait( std::unique_lockstd::mutex& lock )

    ๅ…ˆunlockไน‹ๅ‰่Žทๅพ—็š„mutex๏ผŒ็„ถๅŽ้˜ปๅกžๅฝ“ๅ‰็š„ๆ‰ง่กŒ็บฟ็จ‹ใ€‚

    ๆŠŠๅฝ“ๅ‰็บฟ็จ‹ๆทปๅŠ ๅˆฐ็ญ‰ๅพ…็บฟ็จ‹ๅˆ—่กจไธญ๏ผŒ่ฏฅ็บฟ็จ‹ไผšๆŒ็ปญ block ็›ดๅˆฐ่ขซ notify_all() ๆˆ– notify_one() ๅ”ค้†’ใ€‚

    ่ขซๅ”ค้†’ๅŽ๏ผŒ่ฏฅthreadไผš้‡ๆ–ฐ่Žทๅ–mutex๏ผŒ่Žทๅ–ๅˆฐmutexๅŽๆ‰ง่กŒๅŽ้ข็š„ๅŠจไฝœใ€‚

    ็บฟ็จ‹blockๆ—ถๅ€™ไนŸๅฏ่ƒฝ่ขซๆ„ๅค–ๆˆ–่€…้”™่ฏฏๅ”ค้†’ใ€‚

  • template< class Predicate > void wait( std::unique_lockstd::mutex& lock, Predicate pred );

    ่ฏฅ้‡่ฝฝ่ฎพ็ฝฎไบ†็ฌฌไบŒไธชๅ‚ๆ•ฐ Predicate๏ผŒ ๅชๆœ‰ๅฝ“predไธบfalseๆ—ถ๏ผŒwaitๆ‰ไผš้˜ปๅกžๅฝ“ๅ‰็บฟ็จ‹ใ€‚

    ่ฏฅๆƒ…ๅ†ตไธ‹๏ผŒ็บฟ็จ‹่ขซๅ”ค้†’ๅŽ๏ผŒๅ…ˆ้‡ๆ–ฐๅˆคๆ–ญpred็š„ๅ€ผใ€‚

    ๅฆ‚ๆžœpredไธบfalse๏ผŒๅˆ™ไผš้‡Šๆ”พmutexๅนถ้‡ๆ–ฐ้˜ปๅกžๅœจwaitใ€‚

    ๅ› ๆญค๏ผŒ่ฏฅmutexๅฟ…้กปๆœ‰pred็š„ๆƒ้™ใ€‚่ฏฅ้‡่ฝฝๆถˆ้™คไบ†ๆ„ๅค–ๅ”ค้†’็š„ๅฝฑๅ“ใ€‚

ๅฆ‚ๆžœ็ญ‰ๅพ…็บฟ็จ‹ๅชๆ‰“็ฎ—็ญ‰ๅพ…ไธ€ๆฌก๏ผŒ้‚ฃไนˆๅฝ“ๆกไปถไธบtrueๆ—ถๅฎƒๅฐฑไธไผšๅ†็ญ‰ๅพ…่ฟ™ไธชๆกไปถๅ˜้‡ไบ†๏ผŒ

ๆกไปถๅ˜้‡ๆœชๅฟ…ๆ˜ฏๅŒๆญฅๆœบๅˆถ็š„ๆœ€ไฝณ้€‰ๆ‹ฉใ€‚ๅฆ‚ๆžœ็ญ‰ๅพ…็š„ๆกไปถๆ˜ฏไธ€ไธช็‰นๅฎšๆ•ฐๆฎๅ—็š„ๅฏ็”จๆ€งๆ—ถ๏ผŒ่ฟ™ๅฐคๅ…ถๆญฃ็กฎใ€‚ๅœจ่ฟ™ไธชๅœบๆ™ฏไธญ๏ผŒไฝฟ็”จๆœŸๅ€ผ๏ผˆfuture๏ผ‰ๆ›ดๅˆ้€‚ใ€‚ไฝฟ็”จfuture็ญ‰ๅพ…ไธ€ๆฌกๆ€งไบ‹ไปถใ€‚

่‡ชๆ—‹้”

ๅ‡่ฎพๆˆ‘ไปฌๆœ‰ไธ€ไธชไธคไธชๅค„็†ๅ™จcore1ๅ’Œcore2่ฎก็ฎ—ๆœบ๏ผŒ็Žฐๅœจๅœจ่ฟ™ๅฐ่ฎก็ฎ—ๆœบไธŠ่ฟ่กŒ็š„็จ‹ๅบไธญๆœ‰ไธคไธช็บฟ็จ‹๏ผšT1ๅ’ŒT2ๅˆ†ๅˆซๅœจๅค„็†ๅ™จcore1ๅ’Œcore2ไธŠ่ฟ่กŒ๏ผŒไธคไธช็บฟ็จ‹ไน‹้—ดๅ…ฑไบซ็€ไธ€ไธช่ต„ๆบใ€‚

้ฆ–ๅ…ˆๆˆ‘ไปฌ่ฏดๆ˜Žไบ’ๆ–ฅ้”็š„ๅทฅไฝœๅŽŸ็†๏ผŒไบ’ๆ–ฅ้”ๆ˜ฏๆ˜ฏไธ€็งsleep-waiting็š„้”ใ€‚ๅ‡่ฎพ็บฟ็จ‹T1่Žทๅ–ไบ’ๆ–ฅ้”ๅนถไธ”ๆญฃๅœจcore1ไธŠ่ฟ่กŒๆ—ถ๏ผŒๆญคๆ—ถ็บฟ็จ‹T2ไนŸๆƒณ่ฆ่Žทๅ–ไบ’ๆ–ฅ้”๏ผˆpthread_mutex_lock๏ผ‰๏ผŒไฝ†ๆ˜ฏ็”ฑไบŽT1ๆญฃๅœจไฝฟ็”จไบ’ๆ–ฅ้”ไฝฟๅพ—T2่ขซ้˜ปๅกžใ€‚ๅฝ“T2ๅค„ไบŽ้˜ปๅกž็Šถๆ€ๆ—ถ๏ผŒT2่ขซๆ”พๅ…ฅๅˆฐ็ญ‰ๅพ…้˜Ÿๅˆ—ไธญๅŽป๏ผŒๅค„็†ๅ™จcore2ไผšๅŽปๅค„็†ๅ…ถไป–ไปปๅŠก่€Œไธๅฟ…ไธ€็›ด็ญ‰ๅพ…๏ผˆๅฟ™็ญ‰๏ผ‰ใ€‚ไนŸๅฐฑๆ˜ฏ่ฏดๅค„็†ๅ™จไธไผšๅ› ไธบ็บฟ็จ‹้˜ปๅกž่€Œ็ฉบ้—ฒ็€๏ผŒๅฎƒๅŽปๅค„็†ๅ…ถไป–ไบ‹ๅŠกๅŽปไบ†ใ€‚

่€Œ่‡ชๆ—‹้”ๅฐฑไธๅŒไบ†๏ผŒ่‡ชๆ—‹้”ๆ˜ฏไธ€็งbusy-waiting็š„้”ใ€‚ไนŸๅฐฑๆ˜ฏ่ฏด๏ผŒๅฆ‚ๆžœT1ๆญฃๅœจไฝฟ็”จ่‡ชๆ—‹้”๏ผŒ่€ŒT2ไนŸๅŽป็”ณ่ฏท่ฟ™ไธช่‡ชๆ—‹้”๏ผŒๆญคๆ—ถT2่‚ฏๅฎšๅพ—ไธๅˆฐ่ฟ™ไธช่‡ชๆ—‹้”ใ€‚

ไธŽไบ’ๆ–ฅ้”็›ธๅ็š„ๆ˜ฏ๏ผŒๆญคๆ—ถ่ฟ่กŒT2็š„ๅค„็†ๅ™จcore2ไผšไธ€็›ดไธๆ–ญๅœฐๅพช็Žฏๆฃ€ๆŸฅ้”ๆ˜ฏๅฆๅฏ็”จ๏ผˆ่‡ชๆ—‹้”่ฏทๆฑ‚๏ผ‰๏ผŒ็›ดๅˆฐ่Žทๅ–ๅˆฐ่ฟ™ไธช่‡ชๆ—‹้”ไธบๆญขใ€‚

ไปŽโ€œ่‡ชๆ—‹้”โ€็š„ๅๅญ—ไนŸๅฏไปฅ็œ‹ๅ‡บๆฅ๏ผŒๅฆ‚ๆžœไธ€ไธช็บฟ็จ‹ๆƒณ่ฆ่Žทๅ–ไธ€ไธช่ขซไฝฟ็”จ็š„่‡ชๆ—‹้”๏ผŒ้‚ฃไนˆๅฎƒไผšไธ€่‡ดๅ ็”จCPU่ฏทๆฑ‚่ฟ™ไธช่‡ชๆ—‹้”ไฝฟๅพ—CPUไธ่ƒฝๅŽปๅšๅ…ถไป–็š„ไบ‹ๆƒ…๏ผŒ็›ดๅˆฐ่Žทๅ–่ฟ™ไธช้”ไธบๆญข๏ผŒ่ฟ™ๅฐฑๆ˜ฏโ€œ่‡ชๆ—‹โ€็š„ๅซไน‰ใ€‚

ๅฝ“ๅ‘็”Ÿ้˜ปๅกžๆ—ถ๏ผŒไบ’ๆ–ฅ้”ๅฏไปฅ่ฎฉCPUๅŽปๅค„็†ๅ…ถไป–็š„ไปปๅŠก๏ผ›่€Œ่‡ชๆ—‹้”่ฎฉCPUไธ€็›ดไธๆ–ญๅพช็Žฏ่ฏทๆฑ‚่Žทๅ–่ฟ™ไธช้”ใ€‚้€š่ฟ‡ไธคไธชๅซไน‰็š„ๅฏนๆฏ”ๅฏไปฅๆˆ‘ไปฌ็Ÿฅ้“โ€œ่‡ชๆ—‹้”โ€ๆ˜ฏๆฏ”่พƒ่€—่ดนCPU็š„ใ€‚

// use std::atomic_flag
class spinlock_mutex {
    std::atomic_flag flag;
    public:
    spinlock_mutex(): flag(ATOMIC_FLAG_INIT) {
    }
    void lock() {
        while (flag.test_and_set(std::memory_order_aquire));
    }
    void unlock() {
        flag.clear(std::memory_order_release);
    }
}

่ฏปๅ†™้”

ๆˆ‘ไปฌๅ…่ฎธๅœจๆ•ฐๆฎๅบ“ไธŠๅŒๆ—ถๆ‰ง่กŒๅคšไธชโ€œ่ฏปโ€ๆ“ไฝœ๏ผŒไฝ†ๆ˜ฏๆŸไธ€ๆ—ถๅˆปๅช่ƒฝๅœจๆ•ฐๆฎๅบ“ไธŠๆœ‰ไธ€ไธชโ€œๅ†™โ€ๆ“ไฝœๆฅๆ›ดๆ–ฐๆ•ฐๆฎใ€‚่ฟ™ๅฐฑๆ˜ฏไธ€ไธช็ฎ€ๅ•็š„่ฏป่€…-ๅ†™่€…ๆจกๅž‹ใ€‚

ๅคดๆ–‡ไปถ๏ผšboost/thread/shared_mutex.cpp ็ฑปๅž‹๏ผšboost::shared_lock

ๆไพ›ไธค็ง่ฎฟ้—ฎๆƒ้™็š„ๆŽงๅˆถ๏ผšๅ…ฑไบซๆ€ง๏ผˆshared๏ผ‰ๅ’ŒๆŽ’ไป–ๆ€ง๏ผˆexclusive๏ผ‰ใ€‚

้€š่ฟ‡lock / try_lock่Žทๅ–ๆŽ’ไป–ๆ€ง่ฎฟ้—ฎๆƒ้™๏ผŒ้€š่ฟ‡lock_shared / try_lock_shared่Žทๅ–ๅ…ฑไบซๆ€ง่ฎฟ้—ฎๆƒ้™ใ€‚

่ฟ™ๆ ท็š„่ฎพ็ฝฎๅฏนไบŽๅŒบๅˆ†ไธๅŒ็บฟ็จ‹็š„่ฏปๅ†™ๆ“ไฝœ็‰นๅˆซๆœ‰็”จใ€‚shared_mutexๆ˜ฏc++17ไธญๅผ•ๅ…ฅ็š„๏ผŒไฝฟ็”จๆ—ถ้œ€่ฆๆณจๆ„็ผ–่ฏ‘ๅ™จ็‰ˆๆœฌใ€‚

#include <iostream>
#include <mutex>  // For std::unique_lock
#include <shared_mutex>
#include <thread>

class ThreadSafeCounter {
 public:
  ThreadSafeCounter() = default;

  // Multiple threads/readers can read the counter's value at the same time.
  unsigned int get() const {
    std::shared_lock lock(mutex_);
    return value_;
  }

  // Only one thread/writer can increment/write the counter's value.
  void increment() {
    std::unique_lock lock(mutex_);
    value_++;
  }

  // Only one thread/writer can reset/write the counter's value.
  void reset() {
    std::unique_lock lock(mutex_);
    value_ = 0;
  }

 private:
  mutable std::shared_mutex mutex_;
  unsigned int value_ = 0;
};


int main() {
  ThreadSafeCounter counter;

  auto increment_and_print = [&counter]() {
    for (int i = 0; i < 3; i++) {
      counter.increment();
      std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';

      // Note: Writing to std::cout actually needs to be synchronized as well
      // by another std::mutex. This has been omitted to keep the example small.
    }
  };

  std::thread thread1(increment_and_print);
  std::thread thread2(increment_and_print);

  thread1.join();
  thread2.join();
}

Last updated