关于c++:C并发与多线程-6uniquelock-类模板详解

44次阅读

共计 6445 个字符,预计需要花费 17 分钟才能阅读完成。

unique_lock 取代 lock_guard

  • std::lock_guard 和 std::unique_lock 都能实现主动加锁与解锁性能。
  • std::unique_lock 外部持有 mutex 的状态 (locked,unlocked),因而比 lock_guard 应用更加灵便但同时更占用空间、速度更慢。
// unique_lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;           // mutex for critical section

void print_block (int n, char c) 
{// critical section (exclusive access to std::cout signaled by lifetime of lck):
  std::unique_lock<std::mutex> lck (mtx);
  for (int i=0; i<n; ++i) {std::cout << c;}
  std::cout << '\n';
}

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

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

  return 0;
}

输入:

**************************************************
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

unique_lock 的属性参数

属性 形容
通过调用成员 lock 来锁定互斥对象
try_lock 通过调用成员 try_lock 尝试锁定互斥对象
defer_lock unique_lock 对象不会在结构时主动锁定互斥对象,将其初始化未不领有锁
adopt_lock unique_lock 对象不会在结构时锁定互斥对象,而是假设调用线程曾经领有互斥锁的所有权。

try_to_lock

std::unique_lock<std::mutex>lck (mutex,std::try_to_lock);

defer_lock

std::unique_lock<std::mutex>lck (mutex,std::defer_lock);
// ...
lck.lock();
// ...
lck.unlock();
// ...
lck.lock();

adopt_lock

mutex.lock();
// ......
std::unique_lock<std::mutex>lck (mutex,std::adopt_lock);

std::unique_lock 的成员函数

std::unique_lock::owns_lock()

bool owns_lock() const noexcept;

性能:领有锁

  • 返回对象是否领有锁。
  • 如果托管互斥对象以由 unique_lock 对象锁定,并且尔后未解锁或开释,则返回 true。
  • 在其它状况下,都返回 false。
  • 这是 unique_lock :: operator bool 的别名。

参数

返回值

  • 如果对象在托管互斥对象上领有锁,则返回 true, 否则返回 false。
// unique_lock::operator= example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::try_to_lock

std::mutex mtx;           // mutex for critical section

void print_star () {std::unique_lock<std::mutex> lck(mtx,std::try_to_lock);
  // print '*' if successfully locked, 'x' otherwise:
  if (lck.owns_lock())
    std::cout << '*';
  else
    std::cout << 'x';

    std::cout << '\n';
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<10; ++i)
    threads.emplace_back(print_star);

  for (auto& x: threads) x.join();

  return 0;
}

输入:

*
*
*
*
*
*
*
*
*
*

std::unique_lock::lock()

template <class Mutex1, class Mutex2, class... Mutexes>
  void lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

性能:锁定多个互斥锁

  • 锁定所有作为参数的对象,并在必要时阻塞调用线程。
  • 该函数用一个未指定序列去调用他们的 lock, try_lock 和 unlock 成员锁定对象,以确保所有参数在返回时都被锁定(不产生任何死锁)。
  • 如果该函数无奈锁定所有对象(例如,因为外部调用之一引发了异样),则该函数会在失败之前首先解锁胜利锁定的所有对象(如果有)。

参数

a, b, cde

  • 尝试锁定的对象,Mutex1,Mutex2 和 Mutexes 应该是能够锁定的类型。

返回值

std::unique_lock::unlock()

void unlock();

性能:解锁互斥锁

  • 调用托管互斥对象的成员解锁,并将领有状态设置为 false。
  • 如果在调用之前领有状态为 false,则该函数将引发 system_error 异样。

返回值

// unique_lock::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

std::mutex mtx;           // mutex for critical section

void print_thread_id (int id) {std::unique_lock<std::mutex> lck (mtx,std::defer_lock);
  // critical section (exclusive access to std::cout signaled by locking lck):
  lck.lock();
  std::cout << "thread #" << id << '\n';
  lck.unlock();}

int main ()
{std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_thread_id,i+1);

  for (auto& th : threads) th.join();

  return 0;
}

输入:

thread #1
thread #2
thread #4
thread #3
thread #7
thread #5
thread #6
thread #10
thread #8
thread #9

std::unique_lock::try_lock()

template <class Mutex1, class Mutex2, class... Mutexes>
  int try_lock (Mutex1& a, Mutex2& b, Mutexes&... cde);

性能:尝试去锁定多个对象

  • 尝试应用其 try_lock 成员函数锁定所有作为参数传递的对象(非阻塞)。
  • 该函数为每个参数(首先是 a,而后是 b,最初是 cde 中的其它参数,以雷同的程序)调用 try_lock 成员函数,直到所有调用都胜利或者其中一个调用失败(通过返回谬误或引发异样)。
  • 如果函数因为调用失败而完结,则对 try_lock 调用胜利的所有对象都将解锁,并且该函数返回锁定失败的对象的参数顺序号。不会对参数列表中的其余对象执行进一步的调用。

参数

a, b, cde

  • 尝试锁定的对象,Mutex1,Mutex2 和 Mutexes 应该是能够锁定的类型。

返回值

  • 如果函数胜利锁定了所有对象,则返回 -1。
  • 否则,该函数将返回未定锁定对象的索引(0 示意 a,1 示意 b)
// unique_lock::try_lock example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

std::mutex mtx;           // mutex for critical section

void print_star () {std::unique_lock<std::mutex> lck(mtx,std::defer_lock);
  // print '*' if successfully locked, 'x' otherwise:
  if (lck.try_lock())
    std::cout << '*';
  else
    std::cout << 'x';

    std::cout << '\n';
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<10; ++i)
    threads.emplace_back(print_star);

  for (auto& x: threads) x.join();

  return 0;
}

输入:

*
*
*
*
*
*
*
*
*
*

std::unique_lock::release()

mutex_type* release() noexcept;

性能:开释互斥锁

  • 返回指向退管互斥对象的指针,从而开释对其的所有权。
  • 调用之后,unique_lock 对象不再治理任何互斥对象(即,其状态与默认结构的状态雷同)。
  • 请留神,此函数不会锁定或解锁返回的互斥量。

参数

返回值

  • 指向调用之前由 unique_lock 治理的互斥对象的指针。
// unique_lock::release example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;
int count = 0;

void print_count_and_unlock (std::mutex* p_mtx) {
  std::cout << "count:" << count << '\n';
  p_mtx->unlock();}

void task() {std::unique_lock<std::mutex> lck(mtx);
  ++count;
  print_count_and_unlock(lck.release());
}

int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<10; ++i)
    threads.emplace_back(task);

  for (auto& x: threads) x.join();

  return 0;
}

输入:

count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 10

std::unique_lock::mutex

mutex_type* mutex() const noexcept;

性能:获取互斥量

  • 返回指向托管互斥对象的指针。
  • 请留神,unique_lock 不会开释对托管互斥量的所有权。即,如果它领有互斥锁,则依然负责在某个时刻开释锁(例如销毁它时)

参数

返回值

指向由 unique_lock 治理的互斥对象的指针。

// unique_lock::mutex example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock

class MyMutex : public std::mutex {
  int _id;
public:
  MyMutex (int id) : _id(id) {}
  int id() {return _id;}
};

MyMutex mtx (101);

void print_ids (int id) {std::unique_lock<MyMutex> lck (mtx);
  std::cout << "thread #" << id << "locked mutex" << lck.mutex()->id() << '\n';
}

int main ()
{std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_ids,i+1);

  for (auto& th : threads) th.join();

  return 0;
}

输入:

thread #1 locked mutex 101
thread #2 locked mutex 101
thread #3 locked mutex 101
thread #4 locked mutex 101
thread #7 locked mutex 101
thread #5 locked mutex 101
thread #10 locked mutex 101
thread #8 locked mutex 101
thread #9 locked mutex 101
thread #6 locked mutex 101

std::unique_lock::operator =

move (1)
unique_lock& operator= (unique_lock&& x) noexcept;

copy [deleted] (2)
unique_lock& operator= (const unique_lock&) = delete;

性能:挪动赋值

  • 用 x 中的一个替换托管的互斥对象,包含其领有的状态。
  • 如果对象在调用之前在其托管互斥对象上领有锁,则在替换之前将调用其 unlock 成员。
  • x 放弃与默认结构雷同的状态(指没有托管对象)。
  • unique_lock 对象无奈赋值。

参数

x

  • 另一个 unique_lock 对象。

返回值

  • *this。
// unique_lock::operator= example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock

std::mutex mtx;           // mutex for critical section

void print_fifty () {
  std::unique_lock<std::mutex> lck;         // default-constructed
  lck = std::unique_lock<std::mutex>(mtx);  // move-assigned
  for (int i=0; i<50; ++i)
  {if (lck.owns_lock())
        std::cout << '*';
      else
         std::cout << '#';
  }
  std::cout << '\n';
}

int main ()
{std::thread th1 (print_fifty);

  th1.join();

  return 0;
}

输入:

**************************************************

正文完
 0