第一版,伪代码描述

/
锁实现原理
/
bool available = true;
/

该方法操作需要原子性保证
@param target
@return
/
bool test_and_set(bool target) {
bool oldValue =
target;
*target = false;
return oldValue;
}
void lock() {
while (!test_and_set(&available)) {
//empty loop until it changes.
}
}
void unlock() {
available = true;
}

第二版,atomic库

include
using namespace std;
/
设置 flag 的默认值为ATOMIC_FLAG_INIT 也就是false
/
atomic_flag flag = ATOMIC_FLAG_INIT;
/

test_and_set()方法是原子性操作,保证每次调用都会把原flag值返回,并且将原flag值改为 1(true)
/
void lock() {
while (flag.test_and_set()) {
//empty loop until lock_flag value is 0.
}
}
/*
clear()方法将flag值设置为0(false)
*/
void unlock() {
flag.clear();
}

第三版,pthread提供的pthread_mutex…

1、初始化锁
pthread_mutex_t lock_flag = PTHREAD_MUTEX_INITIALIZER;

或者
pthread_mutex_init(&lock_flag, NULL);

2、进入临界区之前加锁
pthread_mutex_lock(&lock_flag);

3、出临界区的地方释放锁
pthread_mutex_unlock(&lock_flag);

测试方案1

include
#include
#include

using namespace std;
/
设置 flag 的默认值为ATOMIC_FLAG_INIT 也就是false
/
atomic_flag flag = ATOMIC_FLAG_INIT;
/

test_and_set()方法是原子性操作,保证每次调用都会把原flag值返回,并且将原flag值改为 1(true)
/
void lock() {
while (flag.test_and_set()) {
//empty loop until lock_flag value is 0.
}
}
/*
clear()方法将flag值设置为0(false)
*/
void unlock() {
flag.clear();
}

void thread_run(void args) {
int number = static_cast<int >(args);
lock();
printf(“thread number %d : 1 \n “, number);
printf(“thread number %d : 2 \n “, number);
printf(“thread number %d : 3 \n “, number);
unlock();
return 0;
}

/*
创建30个线程对其进行测试,效果如下
如果不加锁,则会出现一个线程打印一条或者俩条之后插入其他线程打印的结果
如果加锁,则会出现每个线程会完整打印完三条语句1,2,3之后再打印其他线程
出现上面情况则证明锁机制成功生效了
@return
*/
int main() {
int thread_sum = 30;
pthread_t tid_arr[thread_sum];
int t_number[thread_sum];
for (int i = 0; i < thread_sum; ++i) {
t_number[i] = i;
//创建线程进行测试
pthread_create(tid_arr + i, NULL, thread_run, t_number + i);
}
for (pthread_t tid : tid_arr) {
pthread_join(tid, NULL);
}
return 0;
}

测试方案2

include
#include

using namespace std;

int ticketAmount = 2; //定义全局变量,订票终端的总票数

pthread_mutex_t lock_flag = PTHREAD_MUTEX_INITIALIZER;

void ticketAgent(void args) {
pthread_mutex_lock(&lock_flag);
int t = ticketAmount;
if (t > 0) {
printf(“卖出一张票 \n”);
t—;
} else {
printf(“没有票了 \n”);
}
ticketAmount = t;
pthread_mutex_unlock(&lock_flag);
pthread_exit(0);
}
/*
逻辑为2张票被2个线程分别减少一次,最终结果应该必定要为0
如果不加锁,最后主线程中会打印出票数不为0的情况
加锁之后,才能保证最终结果一定为0
@return
/
int main() {
pthread_t tid[2];
//lock_flag = PTHREAD_MUTEX_INITIALIZER; //此方式也是对锁的初始化
pthread_mutex_init(&lock_flag, NULL);
for (int i = 0; i < 2; ++i) {
pthread_create(tid + i, NULL, ticketAgent, NULL);
}
for (int i = 0; i < 2; ++i) {
pthread_join(tid[i], 0);
}
printf(“最后票数为:%d \n”, ticketAmount);
return 0;

}

总结

上述内容中,锁原理加锁处用的while循环为空内容,也就是当线程需要进入临界区而没有拿到锁的情况下会占用cpu进行不停的询问锁状态,这种成为自旋锁,并且该处占用cpu称之为空忙等待,与之对应的是让权等待,也就是当没有拿到锁的情况下,让出cpu执行权,进入休眠等待,直到被唤醒继续尝试拿锁。
关于pthread_mutex_init()方法的第二个参数锁属性的说明:
不同的互斥锁的属性在试图对一个已经被锁定的互斥锁加锁时表现不同。有四个值可供选择:

  • PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。