淺談互斥鎖與進程間的通信(舉例說明)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、互斥鎖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程之間數據隔離,但是共享一套文件系統,因而可以通過文件來實現進程直接的通信,但問題是必須自己加鎖處理。注意:加鎖的目的是爲了保證多個進程修改同一塊數據時,同一時間只能有一個修改,即串行的修改,沒錯,速度是慢了,犧牲了速度而保證了數據安全。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1.上廁所的小例子:你上廁所的時候肯定得鎖門吧,有人來了看見門鎖着,就會在外面等着,等你吧門開開出來的時候,下一個人才去上廁所。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"from multiprocessing import Process,Lock\nimport os\nimport time\ndef work(mutex):\n mutex.acquire()\n print('task[%s] 上廁所'%os.getpid())\n time.sleep(3)\n print('task[%s] 上完廁所'%os.getpid())\n mutex.release()\nif __name__ == '__main__':\n mutex = Lock()\n p1 = Process(target=work,args=(mutex,))\n p2 = Process(target=work,args=(mutex,))\n p3 = Process(target=work,args=(mutex,))\n p1.start()\n p2.start()\n p3.start()\n p1.join()\n p2.join()\n p3.join()\n print('主')","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、模擬搶票(也是利用了互斥鎖的原理 :LOCK互斥鎖)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"import json\nimport time\nfrom multiprocessing import Process,Lock\n\n\ndef search(name):\n time.sleep(1)\n dic = json.load(open(\"db.txt\", \"r\", encoding=\"utf-8\"))\n print(\" 查看到的剩餘票數 %s\" % (name, dic[\"count\"]))\n\n\ndef get(name):\n time.sleep(0.1)\n dic = json.load(open(\"db.txt\", \"r\", encoding=\"utf-8\"))\n if dic[\"count\"] > 0:\n dic[\"count\"] -= 1\n time.sleep(0.1)\n json.dump(dic, open(\"db.txt\", \"w\", encoding=\"utf-8\"))\n print(\" 購票成功\" % name)\n else:\n print(\" 購票失敗,已經無剩餘票\" % name)\n\n\ndef task(name, mutex):\n search(name)\n\n mutex.acquire()\n get(name)\n mutex.release()\n\n\nif __name__ == \"__main__\":\n mutex = Lock()\n for i in range(100):\n p = Process(target=task, args=(\"路人%s\" % i, mutex))\n p.start()","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、Process對象的其他屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"p.daemon :","attrs":{}},{"type":"text","text":"守護進程(必須在開啓之前設置守護進程):如果父進程死,子進程p也死了","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"p.join:","attrs":{}},{"type":"text","text":"父進程等p執行完了才運行主進程,是父進程阻塞在原地,而p仍然在後臺運行。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"terminate:","attrs":{}},{"type":"text","text":"強制關閉。(確保p裏面沒有其他子進程的時候關閉,如果裏面有子進程,你去用這個方法強制關閉了就會產生殭屍進程(打個比方:如果你老子掛了,你還沒掛,那麼就沒人給你收屍了,啊哈哈))","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"is_alive:","attrs":{}},{"type":"text","text":"關閉進程的時候,不會立即關閉,所以is_alive立刻查看的結果可能還是存活","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"p.join():","attrs":{}},{"type":"text","text":"父進程在等p的結束,是父進程阻塞在原地,而p仍然在後臺運行","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"p.name:","attrs":{}},{"type":"text","text":"查看名字","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"p.pid :","attrs":{}},{"type":"text","text":"查看id","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、進程間的三種通信(IPC)方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"方式一:隊列(推薦使用)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進程彼此之間互相隔離,要實現進程間通信(IPC),multiprocessing模塊支持兩種形式:隊列和管道,這兩種方式都是使用消息傳遞的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1.隊列:隊列類似於一條管道,元素先進先出","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要注意的一點是:隊列都是在內存中操作,進程退出,隊列清空,另外,隊列也是一個阻塞的形態","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2.隊列分類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隊列有很多種,但都依賴與模塊queue","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"queue.Queue() #先進先出","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"queue.LifoQueue() #後進先出","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"queue.PriorityQueue() #優先級隊列","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"queue.deque() #雙線隊列","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"創建隊列的類(底層就是以管道和鎖定的方式實現):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Queue([maxsize]):創建共享的進程隊列,Queue是多進程安全的隊列,","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用Queue實現多進程之間的數據傳遞","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"參數介紹:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1 maxsize是隊列中允許最大項數,省略則無大小限制。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"方法介紹:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"q.put方法用以插入數據到隊列中,put方法還有兩個可選參數:\nblocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,該方法會阻塞timeout指定的時間,\n直到該隊列有剩餘的空間。如果超時,會拋出Queue.Full異常。如果blocked爲False,\n但該Queue已滿,會立即拋出Queue.Full異常。\nq.get方法可以從隊列讀取並且刪除一個元素。\n同樣,get方法有兩個可選參數:blocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,\n那麼在等待時間內沒有取到任何元素,會拋出Queue.Empty異常。如果blocked爲False,有兩種情況存在,\n如果Queue有一個值可用,則立即返回該值,否則,如果隊列爲空,則立即拋出Queue.Empty異常.\n \nq.get_nowait():同q.get(False)\nq.put_nowait():同q.put(False)\n \nq.empty():調用此方法時q爲空則返回True,該結果不可靠,比如在返回True的過程中,\n如果隊列中又加入了項目。\nq.full():調用此方法時q已滿則返回True,該結果不可靠,比如在返回True的過程中,\n如果隊列中的項目被取走。\nq.qsize():返回隊列中目前項目的正確數量,結果也不可靠,理由同q.empty()和q.full()一樣","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"應用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"# 1.可以往隊列裏放任意類型的\n# 2.先進先出\nfrom multiprocessing import Process,Queue\nq= Queue(3)\nq.put('first') #默認block=True\nq.put('second')\nq.put('third')\n\nprint(q.get())\nprint(q.get())\nprint(q.get())","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"生產者和消費者模型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在併發編程中使用生產者和消費者模式能夠解決絕大多數併發問題。該模式通過平衡生產線程和消費線程的工作能力來提高程序的整體處理數據的速度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"爲什麼要使用生產者和消費者模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在線程世界裏,生產者就是生產數據的線程,消費者就是消費數據的線程。在多線程開發當中,如果生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個問題於是引入了生產者和消費者模式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"什麼是生產者消費者模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之後不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就相當於一個緩衝區,平衡了生產者和消費者的處理能力。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"基於隊列實現生產者消費者模型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個生產者和一個消費者(有兩種方式):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1、q.put(None):生產者給放一個None進去","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"from multiprocessing import Process,Queue\nimport os\nimport time\nimport random\n#首先得有生產者和消費者\n# 生產者製造包子\n'''這種用 q.put(None)放進去一個None的方法雖然解決了問題\n但是如果有多個生產者多個消費者,或許框裏面沒有包子了但是\n還有其他的食物呢,你就已經顯示空着,這樣也可以解決,就是不完美,\n還可以用到JoinableQueue去解決'''\ndef producter(q):\n for i in range(10):\n time.sleep(2) #生產包子得有個過程,就先讓睡一會\n res = '包子%s'%i #生產了這麼多的包子\n q.put(res) #把生產出來的包子放進框裏面去\n print('\\033[44m%s製造了%s\\033[0m'%(os.getpid(),res))\n q.put(None) #只有生產者才知道什麼時候就生產完了(放一個None進去說明此時已經生產完了)\n# 消費者喫包子\ndef consumer(q):\n while True:#假如消費者不斷的喫\n res = q.get()\n if res is None:break #如果喫的時候框裏面已經空了,就直接break了\n time.sleep(random.randint(1,3))\n print('\\033[41m%s吃了%s\\033[0m' % (os.getpid(),res))\nif __name__ == '__main__':\n q = Queue()\n p1 = Process(target=producter,args=(q,))\n p2 = Process(target=consumer,args=(q,))\n p1.start()\n p2.start()\n p1.join()\n p2.join() #等待執行完上面的進程,在去執行主\n print('主')","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2、利用JoinableQueue","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"cpp"},"content":[{"type":"text","text":"from multiprocessing import Process,JoinableQueue\nimport os\nimport time\nimport random\n#首先得有生產者和消費者\n# 消費者喫包子\ndef consumer(q):\n while True:\n res = q.get()\n time.sleep(random.randint(1,3))\n print('\\033[41m%s吃了%s\\033[0m' % (os.getpid(),res))\n q.task_done() #任務結束了(消費者告訴生產者,我已經吧東西取走了)\ndef product_baozi(q):\n for i in range(5):\n time.sleep(2)\n res = '包子%s' % i\n q.put(res)\n print('\\033[44m%s製造了%s\\033[0m' % (os.getpid(), res))\n q.join() #不用put(None) 了,在等q被取完。(如果數據沒有被取完,生產者就不會結束掉)\ndef product_gutou(q):\n for i in range(5):\n time.sleep(2)\n res = '骨頭%s' % i\n q.put(res)\n print('\\033[44m%s製造了%s\\033[0m' % (os.getpid(), res))\n q.join()\ndef product_doujiang(q):\n for i in range(5):\n time.sleep(2)\n res = '豆漿%s' % i\n q.put(res)\n print('\\033[44m%s製造了%s\\033[0m' % (os.getpid(), res))\n q.join()\n\nif __name__ == '__main__':\n q = JoinableQueue()\n # 生產者們:廚師們\n p1 = Process(target=product_baozi,args=(q,))\n p2 = Process(target=product_doujiang,args=(q,))\n p3 = Process(target=product_gutou,args=(q,))\n\n #消費者們:喫貨們\n p4 = Process(target=consumer,args=(q,))\n p5 = Process(target=consumer,args=(q,))\n p4.daemon = True\n p5.daemon = True\n # p1.start()\n # p2.start()\n # p3.start()\n # p4.start()\n # p5.start()\n li = [p1,p2,p3,p4,p5]\n for i in li:\n i.start()\n p1.join()\n p2.join()\n p3.join()\n print('主')","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方式二:管道(不推薦使用,瞭解即可)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"管道相當於隊列,但是管道不自動加鎖","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方式三:共享數據(不推薦使用,瞭解即可)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"共享數據也沒有自動加鎖的功能,所以還是推薦用隊列的。感興趣的可以研究研究管道和共享數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"相關視頻;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.zhihu.com/zvideo/1317207611981500416","title":null},"content":[{"type":"text","text":"Epoll的具體實現與epoll線程安全,互斥鎖,自旋鎖,CAS,原子操作","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章