Сиз спинлоктор жана өз ара блоктор (deadlock) сыяктуу синхрондоштуруу механизмдери менен таанышсызбы?
sobes.tech AI
AIден жооп
Ооба, тааныштам:
Спинлок — бул синхрондоштуруу механизми, анда агым ресурска жетпей калганда, ал үзүнчө циклде анын жеткиликтүүлүгүн үзгүлтүксүз текшерип турат, "айлануу" (spinning), күтүү абалына өтпөстөн. Бул критикалык бөлүмдөрдү коргоодо эффективдүү, алар өтө кыска убакытка ээлениши күтүлөт. Контекст алмаштыруу чыгымдарын азайтат, бирок узак убакыт бою блокировка кармалса, көп процессор убактысын сарптай алат.
Мютекс (англисчеден. mutually exclusive — өз ара ажыратуу) — бул синхрондоштуруу объектиси, ал бир нече агымга бир ресурсты биргелешип колдонууга мүмкүндүк берет, бирок ресурсту ар бир учурда бир гана агым колдонушун камсыздайт. Эгер агым мютекс алууга мүмкүнчүлүк албаса, ал блоктолот жана күтүүгө өтөт, процессорду бошотот. Мютекс критикалык бөлүмдөрдү коргоодо ылайыктуу, алар узак убакыт бою ээлениши мүмкүн.
Өз ара блокировка (deadlock) — бул эки же андан көп агымдар эч качан бири-бирин күтүп, ресурстарды бошотпөй турган абал. Бул Кофман шарттары аткарылганда пайда болот:
- Өз ара ажыратуу: Ресурстар бир эле учурда бир нече агым тарабынан колдонулушу мүмкүн эмес.
- Туурак жана күтүү: Агымдар алгачкы бөлүнгөн ресурстарды кармап, жаңы ресурстарды күтүшөт.
- Мажбурлап бошотуу жок: Ресурстарды мажбурлап алууга болбойт, аларды кармап турган агымдардан.
- Циклдик күтүү: Эки же андан көп агымдар циклик түзүп, ар бири кийинки агымдын ээлеген ресурсуна күтүшөт.
Бул блокировканы алдын алуу үчүн, төмөнкү стратегияларды колдонсо болот:
- Бир эле учурда бир нече блокировкаларды алуу менен алектенбөө.
- Блокировкаларды алуу тартибин аныктоо.
- Аныктоо жана жоюу механизмдерин колдонуу.
C++'да std::atomic_flag колдонуу менен спинлоктун мисалы:
#include <atomic>
#include <thread>
#include <iostream>
class Spinlock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire));
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
Spinlock spin;
void worker() {
spin.lock();
// Критикалык бөлүм
std::cout << "Агым " << std::this_thread::get_id() << " критикалык бөлүмгө кирди\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Эмуляция иши
std::cout << "Агым " << std::this_thread::get_id() << " чыгуу\n";
spin.unlock();
}
// int main() {
// std::thread t1(worker);
// std::thread t2(worker);
// t1.join();
// t2.join();
// return 0;
// }
std::mutex колдонуу менен mutexтин мисалы:
#include <mutex>
#include <thread>
#include <iostream>
std::mutex mtx;
void worker_mutex() {
mtx.lock();
// Критикалык бөлүм
std::cout << "Агым " << std::this_thread::get_id() << " критикалык бөлүмгө кирди\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Эмуляция иши
std::cout << "Агым " << std::this_thread::get_id() << " чыгуу\n";
mtx.unlock();
}
// int main() {
// std::thread t1(worker_mutex);
// std::thread t2(worker_mutex);
// t1.join();
// t2.join();
// return 0;
// }
Мүмкүн болгон өз ара блокировка мисалы:
#include <mutex>
#include <thread>
#include <iostream>
std::mutex mutex1;
std::mutex mutex2;
// Функция, алгач `mutex1`ти ээлеп, андан соң `mutex2`ти ээлейт
void thread1_func() {
mutex1.lock();
std::cout << "Агым 1 mutex1ти ээлеп алды\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Эмуляция иши
mutex2.lock(); // Агым 1 `mutex2`ти күтүп жатат, ал, мүмкүн, агым 2 тарабынан ээленген
std::cout << "Агым 1 mutex2ти ээлеп алды\n";
// Ички критикалык бөлүм эки mutex менен
mutex2.unlock();
mutex1.unlock();
}
// Функция, алгач `mutex2`ти ээлеп, андан соң `mutex1`ти ээлейт
void thread2_func() {
mutex2.lock();
std::cout << "Агым 2 mutex2ти ээлеп алды\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Эмуляция иши
mutex1.lock(); // Агым 2 `mutex1`ти күтүп жатат, ал, мүмкүн, агым 1 тарабынан ээленген
std::cout << "Агым 2 mutex1ти ээлеп алды\n";
// Ички критикалык бөлүм эки mutex менен
mutex1.unlock();
mutex2.unlock();
}
// int main() {
// std::thread t1(thread1_func);
// std::thread t2(thread2_func);
// t1.join();
// t2.join();
// return 0;
// }
Бул мисалда, эгерде агым 1 mutex1ти ээлеп алып, унчукпай калса, ал эми агым 2 mutex2ти ээлеп алып, унчукпай калса, анда, кийинчерээк, агым 1 mutex2ти күтүп калат (ал, мүмкүн, агым 2 тарабынан ээленген), ал эми агым 2 mutex1ти күтүп калат (ал, мүмкүн, агым 1 тарабынан ээленген). Бул өз ара блокировка деп аталат.
Бул маселени алдын алуу үчүн, жөн гана бир функцияда mutex'терди алуу тартибин өзгөртсө болот, мисалы, биринчи mutex1ти, андан соң mutex2ти алуу. Ошентип, эки функция тең бирдей тартипте mutex'терди алат.