C++11coreintermediate
std::unique_lock (C++11 Addition)
C++11 · Modern Revolution · Compiler flag: -std=c++11
Explanation
A flexible RAII mutex wrapper that supports deferred locking, timed locking, recursive locking, and transfer of lock ownership. More versatile than `std::lock_guard`.
Real-World Scenario
Implement a timed lock on a shared resource that gives up if the lock is not acquired within a deadline.
Quick Snippet
snippet.cpp
#include <mutex>
std::mutex mtx;
void timedAccess() {
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
if (lock.try_lock()) {
// access shared resource
}
}Full Example
unique_lock.cpp
// CodeNebula - C++11 Example: std::unique_lock
// Compile: g++ -std=c++11 -Wall -Wextra -pthread -o example unique_lock.cpp
//
// This example demonstrates:
// - std::unique_lock for flexible mutex ownership
// - Deferred locking, try-locking, and timed locking
// - Moving lock ownership between scopes
// - Comparison with std::lock_guard
// - Using unique_lock with std::condition_variable
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
#include <string>
std::mutex printMtx;
void safePrint(const std::string& msg) {
std::lock_guard<std::mutex> lock(printMtx);
std::cout << msg << std::endl;
}
// ============================================================
// 1. Basic unique_lock usage
// ============================================================
std::mutex dataMutex;
int sharedData = 0;
void basicUsage() {
std::unique_lock<std::mutex> lock(dataMutex); // Locks immediately
sharedData += 10;
safePrint(" Basic: sharedData = " + std::to_string(sharedData));
// Unlocks automatically at end of scope
}
// ============================================================
// 2. Deferred locking
// ============================================================
void deferredLocking() {
std::unique_lock<std::mutex> lock(dataMutex, std::defer_lock);
// Mutex NOT locked yet
safePrint(" Deferred: doing prep work (unlocked)");
lock.lock(); // Now lock it explicitly
sharedData += 5;
safePrint(" Deferred: sharedData = " + std::to_string(sharedData));
lock.unlock(); // Can unlock early
safePrint(" Deferred: doing cleanup (unlocked)");
}
// ============================================================
// 3. try_lock with unique_lock
// ============================================================
std::mutex tryMutex;
void tryLockDemo(const std::string& name) {
std::unique_lock<std::mutex> lock(tryMutex, std::try_to_lock);
if (lock.owns_lock()) {
safePrint(" " + name + ": acquired lock");
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} else {
safePrint(" " + name + ": could not acquire lock");
}
}
// ============================================================
// 4. Timed locking with std::timed_mutex
// ============================================================
std::timed_mutex timedMutex;
void timedLockDemo(const std::string& name) {
std::unique_lock<std::timed_mutex> lock(timedMutex, std::defer_lock);
if (lock.try_lock_for(std::chrono::milliseconds(100))) {
safePrint(" " + name + ": got timed lock");
std::this_thread::sleep_for(std::chrono::milliseconds(50));
} else {
safePrint(" " + name + ": timed out waiting for lock");
}
}
// ============================================================
// 5. Moving lock ownership
// ============================================================
std::unique_lock<std::mutex> acquireLock(std::mutex& mtx) {
std::unique_lock<std::mutex> lock(mtx);
safePrint(" Lock acquired in function");
return lock; // Move ownership to caller
}
// ============================================================
// 6. Manual lock/unlock control
// ============================================================
void manualControl() {
std::unique_lock<std::mutex> lock(dataMutex);
sharedData += 1;
safePrint(" Manual: locked, sharedData = " + std::to_string(sharedData));
lock.unlock();
safePrint(" Manual: unlocked for non-critical section");
lock.lock();
sharedData += 1;
safePrint(" Manual: re-locked, sharedData = " + std::to_string(sharedData));
}
int main() {
std::cout << "=== std::unique_lock ===" << std::endl;
// Basic
std::cout << "\n--- Basic usage ---" << std::endl;
basicUsage();
// Deferred
std::cout << "\n--- Deferred locking ---" << std::endl;
deferredLocking();
// try_to_lock
std::cout << "\n--- try_to_lock ---" << std::endl;
std::thread t1(tryLockDemo, "Alpha");
std::thread t2(tryLockDemo, "Beta");
t1.join();
t2.join();
// Timed lock
std::cout << "\n--- Timed locking ---" << std::endl;
std::thread t3(timedLockDemo, "Fast");
std::thread t4(timedLockDemo, "Slow");
t3.join();
t4.join();
// Move ownership
std::cout << "\n--- Moving lock ownership ---" << std::endl;
{
auto lock = acquireLock(dataMutex);
sharedData += 100;
safePrint(" Caller holds lock, sharedData = " + std::to_string(sharedData));
}
safePrint(" Lock released by caller's scope exit");
// Manual control
std::cout << "\n--- Manual lock/unlock ---" << std::endl;
manualControl();
// Query lock state
std::cout << "\n--- Lock state queries ---" << std::endl;
std::unique_lock<std::mutex> lock(dataMutex, std::defer_lock);
std::cout << " owns_lock (deferred): " << lock.owns_lock() << std::endl;
lock.lock();
std::cout << " owns_lock (locked): " << lock.owns_lock() << std::endl;
lock.unlock();
std::cout << " owns_lock (unlocked): " << lock.owns_lock() << std::endl;
return 0;
}
// Expected output (thread ordering may vary):
// === std::unique_lock ===
//
// --- Basic usage ---
// Basic: sharedData = 10
//
// --- Deferred locking ---
// Deferred: doing prep work (unlocked)
// Deferred: sharedData = 15
// Deferred: doing cleanup (unlocked)
//
// --- try_to_lock ---
// Alpha: acquired lock
// Beta: could not acquire lock
//
// --- Timed locking ---
// Fast: got timed lock
// Slow: got timed lock
//
// --- Moving lock ownership ---
// Lock acquired in function
// Caller holds lock, sharedData = 116
// Lock released by caller's scope exit
//
// --- Manual lock/unlock ---
// Manual: locked, sharedData = 117
// Manual: unlocked for non-critical section
// Manual: re-locked, sharedData = 118
//
// --- Lock state queries ---
// owns_lock (deferred): 0
// owns_lock (locked): 1
// owns_lock (unlocked): 0
Run command
g++ -std=c++11 -Wall -Wextra -pthread -o example unique_lock.cpp