Table of contents
1.2 Classification of Design Patterns
1.4 Singleton mode code demonstration
2.3.1 Initialization interface
2.3.4 Open the lock in read mode
4. Optimistic locking and pessimistic locking
1 Design Patterns
1.1 Concept
Design patterns are solutions to common problems that software developers face during the software development process. These solutions are the result of a considerable period of trial and error by numerous software developers.
1.2 Classification of Design Patterns
Creational Patterns: These design patterns provide a way to hide the creation logic while creating an object, instead of using the new operator to instantiate the object directly. This makes the program more flexible in deciding which objects need to be created for a given instance.
For example: factory pattern, singleton pattern
Structural Patterns: These design patterns focus on the composition of classes and objects. The concept of inheritance is used to compose interfaces and to define the way in which composite objects obtain new functionality.
For example: adapter mode, bridge mode
Behavioral Patterns: These design patterns are specifically concerned with communication between objects.
For example: command mode, observer mode
1.3 Singleton Pattern
1. A singleton class can only have one instance.
2. A singleton class must create its own unique instance by itself.
3. The singleton class must provide this instance to all other objects.
Intent: To ensure that there is only one instance of a class, and to provide a global access point to it.
The main solution: a globally used class is frequently created and destroyed.
When to use: When you want to control the number of instances and save system resources.
How to solve: Determine whether the system already has this singleton, if so, return it, if not, create it.
Key code: The constructor is private.
1.4 Singleton mode code demonstration
1.4.1 Lazy Man Mode
///lazy mode #include<iostream> #include<pthread.h> using namespace std; class Sigleton{ private: Sigleton(){}; static Sigleton* st; static pthread_mutex_t g_lock; public: static Sigleton* SinFun(); }; pthread_mutex_t Sigleton::g_lock; Sigleton* Sigleton::st=NULL; Sigleton* Sigleton::SinFun(){ if(st==NULL){ pthread_mutex_lock(&Sigleton::g_lock); if(st==NULL){ st=new Sigleton; } pthread_mutex_unlock(&Sigleton::g_lock); } return st; } int main(){ Sigleton* st=Sigleton::SinFun(); Sigleton* st1=Sigleton::SinFun(); if(st==st1){ cout<<"st == st1"<<endl; } return 0; }
1.4.2 Hungry Man Mode
#include<iostream> using namespace std; class Sigleton{ private: Sigleton(){ } static Sigleton* st; public: static Sigleton* GetInstance(); void Print(){ cout<<"The object is created when the program starts"<<endl; } }; Sigleton* Sigleton::st =new Sigleton; Sigleton* Sigleton::GetInstance(){ return st; } int main(){ Sigleton* st=Sigleton::GetInstance(); st->Print(); return 0; }
2. Read-write lock
2.1 Concept
The read-write lock is an upgraded version of the mutex, which is suitable for a large number of reads and a small amount of writes. It provides better parallelism than mutexes. Because after locking in read mode, when other threads try to lock in read mode, these threads will not be blocked, but read-write locks are more complicated and expensive.
Three states of a read-write lock: (1) Locked in read mode (2) Locked in write mode (equivalent to a mutex) (3) Unlocked
2.2 Locking rules
Only one thread can occupy the read-write lock in write mode at a time. When one execution stream is writing, other execution streams can neither write nor read, and can only be stuck in a blocking state.
Multiple reading threads can occupy the read-write lock in read mode at the same time. There is a reference counter inside the read-write lock, which records how many threads are currently locked in read mode. Whenever a thread opens a read-write lock in read mode, the reference counter is +1, and when a thread releases a read-write lock opened in read mode, the reference counter is -1
Role: to determine whether the read-write lock opened by the read mode can be fully unlocked when the read-write lock is released
If the reference counter is completely decremented to 0, it means that no thread in read mode currently occupies the read-write lock, then the read-write lock in read mode is unlocked, and the reference counter is greater than 0, indicating that there are threads that open read-write locks in read mode, Will be mutually exclusive with the read-write lock you want to open in write mode.
2.3 Interface
2.3.1 Initialization interface
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr); parameter pthread_rwlock_t : Read-write lock type rwlock: Read-write lock object attr: attribute value
2.3.2 Destruction interface
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); parameter rwlock:The passed mutex object
2.3.3 Unlock interface
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); parameter rwlock: The passed mutex object
2.3.4 Open the lock in read mode
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); //block int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); //non-blocking parameter rwlock:The passed mutex object
2.3.5 Lock in write mode
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); //non-blocking interface int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); //blocking interface parameter rwlock:The passed read-write lock object
3. Spin lock
The difference between spin lock (busy-waiting type) and mutex (sleep-waiting type):
1. When the spin lock is locked, if the lock cannot be added, the thread will not switch (if the time slice has not arrived, the thread will switch when the time slice arrives), similar to the while loop taking the lock, it will continue to try to take the lock, until the spin lock is obtained.
2. When the mutex is locked, if the lock cannot be added, the thread will switch (the time slice does not arrive, it will also switch), enter the sleep state, and wake up when other threads release the mutex (unlock). After switching back, grab the lock.
3. Advantages of white spin locks: Because white spin locks do not cause the caller to sleep, the efficiency of spin locks is much higher than that of mutex locks.
4. Disadvantages of spin locks:
The spin lock always occupies the CPU. It keeps running (spinning) without acquiring the lock, so it occupies the CPU. If the lock cannot be obtained in a short time, this will undoubtedly reduce the CPU efficiency.
5. It is suitable for the case where the code in the critical section is short (to put it bluntly: the execution time of the code in the critical section is short), and the efficiency of using the spin lock is relatively high. Because threads don't switch back and forth.
6. When the execution time in the critical section is long, the spin lock is not applicable, because if the lock cannot be obtained, it will occupy the CPU and keep preempting the lock.
4. Optimistic locking and pessimistic locking
Pessimistic lock: When a thread accesses the critical section to modify data, it will be considered that there are parallel modifications by other threads, so the lock is performed before the thread modifies the data, so that multiple threads can access mutually exclusive access. Common pessimistic locks are: mutex, read-write lock, spin lock, etc.
Optimistic locking: When a thread accesses a critical section to modify data, it is optimistic that only the thread is modifying, and there is a high probability that there will be no parallelism. Therefore, the modified data is not locked, but after the modification is completed, a judgment is made, such as version number control, and CAS has nothing to program.