多线程、进程锁机制的作用
程序员文章站
2022-06-10 20:49:45
...
为了最大程度上了解锁的机制,此处使用C++来进行编码
(python其实也比较好哈哈哈哈)
以下代码为不加锁的时候的代码,毫无疑问是乱序的,因为两个线程同时操作了全局变量
#include <thread>
#include <iostream>
#include <mutex>
#include <list>
// 全局变量
std::mutex my_mutex;
list<int> L;
// 写入函数
void func1() {
for (int i = 0; i < 1000; i++) {
std::cout << "插入的数据为:" << i << std::endl;
L.push(i);
}
}
// 读取函数
void func2() {
for(int i = 0; i < 1000; i++) {
std::cout << "读取的数据为:" << L.front() << endl;
L.pop_front();
}
}
int main(int argc, char const* argv[]) {
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
return 0;
}
下面有两种加锁方式,一种是全部写入再全部读出
#include <thread>
#include <iostream>
#include <mutex>
#include <list>
// 全局变量
std::mutex my_mutex;
std::list<int> L;
// 写入函数
void func1() {
std::lock_guard<std::mutex> g1(my_mutex);
for (int i = 0; i < 1000; i++) {
std::cout << "插入的数据为:" << i << std::endl;
L.push_back(i);
}
}
// 读取函数
void func2() {
std::lock_guard<std::mutex> g1(my_mutex);
for (int i = 0; i < 1000; i++) {
if (!L.empty()) {
std::cout << "读取的数据为:" << L.front() << std::endl;
L.pop_front();
}
}
}
int main(int argc, char const* argv[]) {
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
return 0;
}
可以看到,完全没有问题
第二种是一边写入一边读出!!
#include <thread>
#include <iostream>
#include <mutex>
#include <list>
// 全局变量
std::mutex my_mutex;
std::list<int> L;
// 写入函数
void func1() {
// std::lock_guard<std::mutex> g1(my_mutex);
for (int i = 0; i < 1000; i++) {
std::cout << "插入的数据为:" << i << std::endl;
std::lock_guard<std::mutex> g1(my_mutex);
L.push_back(i);
}
}
// 读取函数
void func2() {
// std::lock_guard<std::mutex> g2(my_mutex);
for (int i = 0; i < 1000; i++) {
std::lock_guard<std::mutex> g2(my_mutex);
if (!L.empty()) {
std::cout << "读取的数据为:" << L.front() << std::endl;
L.pop_front();
}
}
}
int main(int argc, char const* argv[]) {
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
return 0;
}
这个过程我来稍微解释一下蛤:
- 对两个线程进行阻塞,防止主线程执行完之前子线程没有执行完
- 第一个子线程在循环内加锁后对全局变量L进行写入一个数据,然后解锁,此时L中有一个数据即1
- 全局变量此时是被解锁的被释放的资源,而此时有两个线程是阻塞的状态,在等待事件,而此时L是被释放,而在两个子线程之中都可以对L进行操作,此时就要竞争资源,哪一个线程先运行就先进行加锁和操作再解锁
- 依次循环往复,直到竞争的次序已经耗尽,所以线程结束。
- 注意:线程在竞争资源的时候线程也是在运行的,只是阻塞的时候在等待事件,如果竞争成功了可以进入加锁,没有竞争成功只有失去了这一次机会,每次只有一个线程可以向前执行
之前看到一句话挺有道理:
如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以向前执行
python的话我也不介意写一下
第一种情况
from threading import Thread
from threading import Lock
from queue import Queue
# 定义全局变量
list_thread = []
lock = Lock()
def func1():
lock.acquire()
for i in range(100):
list_thread.append(i)
print("插入的数据为:", i)
lock.release()
def func2():
lock.acquire()
for i in range(100):
num = list_thread[-1]
list_thread.pop(i)
print("删除的数据为:", num)
lock.release()
def main():
t1 = Thread(target=func1)
t2 = Thread(target=func2)
if __name__ == '__main__':
main()
第二种情况
from threading import Thread
from threading import Lock
from queue import Queue
# 定义全局变量
list_thread = []
lock = Lock()
def func1():
# lock.acquire()
for i in range(100):
lock.acquire()
list_thread.append(i)
lock.release()
print("插入的数据为:", i)
# lock.release()
def func2():
# lock.acquire()
for i in range(100):
lock.acquire()
num = list_thread[-1]
list_thread.pop(i)
lock.release()
print("删除的数据为:", num)
# lock.release()
def main():
t1 = Thread(target=func1)
t2 = Thread(target=func2)
if __name__ == '__main__':
main()
但是python里面有个好东西可以解决加锁的问题,queue。
简单的来说就是多线程需要加锁,很可能会造成死锁,而queue自带锁。所以多线程结合queue会好的很多
queue可以传递多线程的结果,用以代替return
import threading
import time;
from queue import Queue
def job(vec,res):
for i in range(len(vec)):
vec[i] = vec[i]*2
res.put(vec)
def main():
res = Queue()
data=[[1,2,3],[3,4,5],[2,2,2],[3,3,3]]
for i in range(len(data)):
t=threading.Thread(target=job,args=(data[i],res))
t.start()
t.join()
result =[]
for j in range(len(data)):
result.append(res.get())
print(result)
if __name__ == '__main__':
main()
[[2, 4, 6], [6, 8, 10], [4, 4, 4], [6, 6, 6]]
上一篇: 静态代理&动态代理
下一篇: C#验证集合是否存在相同值