1. 程式人生 > 其它 >多執行緒、程序鎖機制的作用

多執行緒、程序鎖機制的作用

技術標籤:多執行緒程式設計多執行緒

為了最大程度上了解鎖的機制,此處使用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;
}

這個過程我來稍微解釋一下蛤:

  1. 對兩個執行緒進行阻塞,防止主執行緒執行完之前子執行緒沒有執行完
  2. 第一個子執行緒在迴圈內加鎖後對全域性變數L進行寫入一個數據,然後解鎖,此時L中有一個數據即1
  3. 全域性變數此時是被解鎖的被釋放的資源,而此時有兩個執行緒是阻塞的狀態,在等待事件,而此時L是被釋放,而在兩個子執行緒之中都可以對L進行操作,此時就要競爭資源,哪一個執行緒先執行就先進行加鎖和操作再解鎖
  4. 依次迴圈往復,直到競爭的次序已經耗盡,所以執行緒結束。
  5. 注意:執行緒在競爭資源的時候執行緒也是在執行的,只是阻塞的時候在等待事件,如果競爭成功了可以進入加鎖,沒有競爭成功只有失去了這一次機會,每次只有一個執行緒可以向前執行

之前看到一句話挺有道理:
如果釋放互斥鎖時有多個執行緒阻塞,所有在該互斥鎖上的阻塞執行緒都會變成可執行狀態,第一個變為執行狀態的執行緒可以對互斥量加鎖,其他執行緒將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用。在這種方式下,每次只有一個執行緒可以向前執行

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]]