Spin locks are available in both Windows and POSIX, and we can also implement spin locks with c++11 Atomic. So what is the relationship between the two properties? Introduce the implementation code first:
#ifndef __spinlock_h__#define__spinlock_h__#include<atomic>#ifdef _win32#include<Windows.h>classspinlock_mutex{ Public: Staticconstexpr DWORD Spinlock_count =-1; Public: //during initialization, there is a problem with insufficient resources, which is ignored here .//specific reference critical Sections and Error handling (Windows via C + +)Spinlock_mutex () {InitializeCriticalSectionAndSpinCount (&M_cs, Spinlock_count); } ~Spinlock_mutex () {deletecriticalsection (&M_cs); } void Lock() {entercriticalsection (&M_cs); } BOOLTry_lock () {returnTryEnterCriticalSection (&m_cs) = =TRUE; } voidunlock () {LeaveCriticalSection (&M_cs); }Private: critical_section M_cs;};#elifDefined (_posix_c_source)#include<pthread.h>classspinlock_mutex{ Public: //there is no handling of possible call errorsSpinlock_mutex () {Pthread_spin_init (&M_cs, pthread_process_private); } ~Spinlock_mutex () {Pthread_spin_destroy (&M_cs); } void Lock() {Pthread_spin_lock (&M_cs); } BOOLTry_lock () {returnPthread_spin_trylock (&m_cs) = =0; } voidunlock () {Pthread_spin_unlock (&M_cs); }Private: pthread_spinlock_t M_cs;};#elseclassspinlock_mutex{std::atomic_flag Flag; Public: Spinlock_mutex (): flag{atomic_flag_init} {}void Lock() { while(Flag.test_and_set (Std::memory_order_acquire)); } voidunlock () {flag.clear (std::memory_order_release); } BOOLTry_lock () {return!Flag.test_and_set (Std::memory_order_acquire); }};#endif#endif //__spinlock_h__
Here is a simple test, two sets of threads, one for inserting and the other for taking out. The test results show:
(1) Both Windows and POSIX-provided C-language spin locks are similar to those used by c++11 to build spin locks using atomic.
(2) with the same number of insert threads and the number of threads removed, the more threads, the less efficient.
Here is the test code:
#include <memory>#include<cassert>#include<iostream>#include<vector>#include<thread>#include<future>#include<random>#include<chrono>#include"spinlock.h"#include<forward_list>structstudent_name{Student_name (intAge =0): Age, Next (nullptr) {}intAge ; Student_name*next;}; Spinlock_mutex g_mtx;std::forward_list<int>g_students;std::atomic<int> g_inserts;//Insert num (successful)std::atomic<int> g_drops;//Drop num (successful)std::atomic<int> g_printnum;//As same as G_dropsstd::atomic<Long Long> g_ageinsum;//Age sum when producing student_namestd::atomic<Long Long> g_ageoutsum;//Age sum when consuming student_namestd::atomic<BOOL> GoOn (true); constexprintInsert_thread_num =1; constexprintDrop_thread_num =1; constexprintOne_thread_produce_num =5000000;//When testing, no more than this number, you know 20,000,00 * * * ~= max_int if thread num <=inlinevoidPrintone (student_name*t) {G_printnum.fetch_add (1, std::memory_order_relaxed); G_ageoutsum.fetch_add (t-Age , std::memory_order_relaxed); G_drops.fetch_add (1, std::memory_order_relaxed); Deletet;}voidInsert_students (intIdno) {std::d efault_random_engine dre (Time (nullptr)); Std::uniform_int_distribution<int> AgeDi (1, About); for(inti =0; i < One_thread_produce_num; ++i) {intNewAge =ageDi (DRE); G_ageinsum.fetch_add (NewAge, std::memory_order_relaxed); {Std::lock_guard<spinlock_mutex>Lock(G_MTX); G_students.push_front (NewAge); } //Use memory_order_relaxed avoiding affect folly memory orderG_inserts.fetch_add (1, std::memory_order_relaxed); }}voidDrop_students (intIdno) { while(Auto go =goon.load (Std::memory_order_consume)) {{Std::forward_list<int>tmp; {Std::lock_guard<spinlock_mutex>Lock(G_MTX); Std::swap (g_students, TMP); } Auto It=Tmp.begin (); while(It! =Tmp.end ()) {G_printnum.fetch_add (1, std::memory_order_relaxed); G_ageoutsum.fetch_add (*it, std::memory_order_relaxed); G_drops.fetch_add (1, std::memory_order_relaxed); ++it; } } }}intMain () {Auto start=Std::chrono::system_clock::now (); Std::vector<std::future<void>>insert_threads; Std::vector<std::future<void>>drop_threads; for(Auto i =0; I! = Insert_thread_num; ++i) {insert_threads.push_back (Std::async (Std::launch::async, insert_students, i)); } for(Auto i =0; I! = Drop_thread_num; ++i) {drop_threads.push_back (Std::async (Std::launch::async, drop_students, i)); } for(auto&thread:insert_threads) {Thread.Get(); } std::this_thread::sleep_for (Std::chrono::milliseconds ( +)); Goon.store (false, std::memory_order_release); for(auto&thread:drop_threads) {Thread.Get(); } {std::forward_list<int>tmp; {Std::lock_guard<spinlock_mutex>Lock(G_MTX); Std::swap (g_students, TMP); } Auto It=Tmp.begin (); while(It! =Tmp.end ()) {G_printnum.fetch_add (1, std::memory_order_relaxed); G_ageoutsum.fetch_add (*it, std::memory_order_relaxed); G_drops.fetch_add (1, std::memory_order_relaxed); ++it; }} Auto End=Std::chrono::system_clock::now (); Std::chrono::d uration<Double> diff = end-start; Std::cout<<"Time to insert and drop is:"<< Diff.count () <<"s\n"; Std::cout<<"Insert Count1:"<< g_inserts.load () <<Std::endl; Std::cout<<"Drop Count1:"<< g_drops.load () <<Std::endl; Std::cout<<"Print Num1:"<< g_printnum.load () <<Std::endl; Std::cout<<"Age in1:"<< g_ageinsum.load () <<Std::endl; Std::cout<<"Age OUT1:"<< g_ageoutsum.load () <<Std::endl; Std::cout<<Std::endl;}
For optional locks, the following information is required:
(1) Application layer with Spinlock the biggest problem is not the same as kernel off interrupt (cli/sti), assuming that the concurrency is slightly more points, thread 1 in the lock after the unlock occurred clock interrupt,
* It will not be cut back for a while. Call unlock, then another thread that calls lock in this time does not hollow run while? This is where CPU time is most wasted.
* So can not shut down the interruption can only sleep, how there are huge conflict costs.
(2) Specific reference: https://www.zhihu.com/question/55764216
Spin locks available in Windows and Pthread