Lock-free (неблокирующие) структуры данных - это подход к многопоточной синхронизации, который обеспечивает прогресс хотя бы одного потока без использования традиционных мьютексов. Основные характеристики:
Non-blocking: Ни один поток не может заблокировать другие
Progress гарантии:
Атомарные операции:
Основа большинства lock-free алгоритмов:
template<typename T>
bool atomic_compare_exchange(std::atomic<T>* obj, T* expected, T desired) {
return obj->compare_exchange_strong(*expected, desired);
}
Для безопасного освобождения памяти:
// Примерная реализация hazard pointer
std::atomic<void*> hazard_pointers[MAX_THREADS];
void retire_node(Node* old) {
// Добавляем в отложенное удаление
// Проверяем, не используется ли в hazard pointers
}
Полезен для структур с частым чтением:
// Читатель:
rcu_read_lock();
// доступ к данным
rcu_read_unlock();
// Писатель:
old_data = atomic_load(&shared_data);
new_data = create_updated_copy(old_data);
atomic_store(&shared_data, new_data);
synchronize_rcu(); // Ждем завершения всех читателей
free(old_data);
template<typename T>
class LockFreeQueue {
struct Node {
std::atomic<Node*> next;
T data;
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
void enqueue(T data) {
Node* newNode = new Node{nullptr, data};
Node* oldTail = tail.load();
while(true) {
Node* next = oldTail->next.load();
if(!next) {
if(oldTail->next.compare_exchange_weak(next, newNode)) {
tail.compare_exchange_weak(oldTail, newNode);
return;
}
} else {
tail.compare_exchange_weak(oldTail, next);
}
}
}
};
template<typename T>
class LockFreeStack {
struct Node {
T data;
Node* next;
};
std::atomic<Node*> head;
public:
void push(T data) {
Node* newNode = new Node{data, head.load()};
while(!head.compare_exchange_weak(newNode->next, newNode));
}
bool pop(T& result) {
Node* oldHead = head.load();
while(oldHead &&
!head.compare_exchange_weak(oldHead, oldHead->next))) {
oldHead = head.load();
}
if(!oldHead) return false;
result = oldHead->data;
delete oldHead;
return true;
}
};
ABA проблема:
struct TaggedPointer {
Node* ptr;
uintptr_t tag;
};
std::atomic<TaggedPointer> head;
Проблема памяти:
Производительность:
struct alignas(64) PaddedAtomic { // Для 64-байтовых кеш-линий
std::atomic<int> value;
};
Преимущества:
Недостатки:
Резюмируем: lock-free структуры данных требуют глубокого понимания memory model и атомарных операций, но обеспечивают существенные преимущества в высоконагруженных системах. Их использование оправдано при реальных bottleneck'ах синхронизации, а не "на всякий случай".