python --- 線程

1. 多任務 - 線程

參考
首先考慮一個沒有多任務的程序:

import timedef sing():# 唱歌 5 秒鐘for i in range(5):print("-----菊花臺ing....-----")time.sleep(1)def dance():# 跳舞 5秒鐘for i in range(5):print("-----跳舞.....-----")time.sleep(5)def main():sing()dance()if __name__ == "__main__":main()

此時,你想同時執行唱歌、跳舞是無法做到的。如果想要同時執行,可以使用python提供的Thread來完成.

import time
from threading import Threaddef sing():# 唱歌 5 秒鐘for i in range(5):print("-----菊花臺ing....-----")time.sleep(1)def dance():# 跳舞 5秒鐘for i in range(5):print("-----跳舞.....-----")time.sleep(1)def main():t1 = Thread(target=sing)t2 = Thread(target=dance)t1.start()t2.start()if __name__ == "__main__":main()

關鍵點:

  • from threading import Thread: 從threading包鐘導入Thread
  • t1 = Thread(target=sing): 使用這個將函數變為線程執行的函數~

1.1 多任務的概念

上面體驗了如何同時執行2個異步函數~下面補充一下多任務的概念.

簡單地說,就是操作系統可以同時運行多個任務.例如: 一邊逛瀏覽器,一遍聽音樂

單核CPU執行多任務: 單核CPU執行多任務的關鍵在于,將cpu的時間切片. 任務1執行0.01秒,然后任務2執行0.01秒,在切到任務1執行0.01秒。由于CPU的運算速度很快,因此,我們感覺就行所有任務都在同時執行一樣

多核CPU執行多任務: 真的并行執行多任務只能在多核CPU上實現. 但是,在平常的代碼中,任務數量會遠遠的大于CPU的核心數,因此操作系統會將任務輪流調度到各個核心上執行~

并行: 同一時刻真正運行在不同的CPU上的任務

并發: 在一個很短的時間內,利用CPU的告訴運轉.執行多個任務

1.2 查看當前線程數量

在某些情況下,需要查看當前程序中的線程數量,可以使用threading.enumerate()進行嘗試

import threading
from time import sleep, ctimedef sing():for i in range(3):print("正在唱第%d首哥" % i)sleep(2)def dance():for i in range(3):print("正在跳第%d支舞" %i)sleep(2)if __name__ == "__main__":print("開始時間: %s" % ctime())t1 = threading.Thread(target=sing)t2 = threading.Thread(target=dance)t1.start()t2.start()while True:length = threading.enumerate()print("當前的總線程數為: %d" % length)if length <= 1:breaksleep(1)print("結束時間: %s" % ctime())

注意:

  • 當調用Thread的時候,不會創建線程
  • 當調用Thread創建出來的實例對象的 start方法時,才會創建線程以及讓這個線程開始運行

1.3 創建線程的第二種方法

第一種方法是通過: t = Thread(target = 函數名)來準備, t.start()來啟動

第二種方法是通過類繼承threading.Thread來實現,代碼如下:

import threading
import timeclass MyThread(threading.Thread):def run(self):for i in range(3):time.sleep(1)msg = "I`m " + self.name + " @" + str(i)print(msg)if __name__ == "__main__":t = MyThread()t.start()

說明:

  1. 類的繼承: class MyThread(threading.Thread)
  2. 通過類的方式創建的線程,必須在該類中定義run函數. 這樣當調用t.start()時候,新創建的線程會去類中尋找run函數并執行
  3. 一個t=MyThread()只會準備一個線程,當t.start()時,會創建線程~

1.4 線程共享全局變量

有些時候,多個不同的線程可能需要用到同一個變量,下面演示全局變量在多線程中的使用

from threading import Thread
import timedef work1():global g_numfor i in range(3):g_num += 1print("---- in work1, g_num is %d ---" % g_num)def work2():global g_numprint("---- in work2, g_num is %d ---" % g_num)def main():g_num = 100print("---- 線程創建執行之前 g_num is %d ---" % g_num)t1 = Thread(target=work1)t1.start()# 讓 t1線程先執行time.sleep(1)t2 = Thread(target=work2)t2.start()if __name__ == "__main__":main()

注意:

  • 在一個函數中對全局變量進行修改的時候需要看是否對全局變量的指向進行了修改
    • 如果修改了指向,那么必須使用global
    • 如果僅修改了指向中的數據,則可以省略global

1.5 帶參數的線程調用

在調用的時候,可能需要傳遞參數進去.這就需要在線程準備的時候,使用args傳遞參數

from threading import Thread
from time import sleepdef test1(tmp):tmp.append(33)def test2(tmp):tmp.append(66)def main():num_arr = [11, 22]print(str(num_arr))t1 = Thread(target=test1, args=(num_arr,))t2 = Thread(target=test2, args=(num_arr,))t1.start()sleep(1)print(str(num_arr))t2.start()print(str(num_arr))if __name__ == "__main__":main()

注意:

  • 多任務共享數據的原因: 多個任務合作同時完成一個大任務~
    • 一個任務獲取數據
    • 一個任務處理數據
    • 一個任務發送數據

1.6 資源競爭

共享變量會產生一個資源競爭的問題: 多個線程同時對一個全局變量進行修改~下面復現問題

import threading
import timeg_num = 0def test1(num):global g_numfor i in range(num):g_num += 1print("test1: g_num: %d" % g_num)def test2(num):global g_numfor i in range(num):g_num += 1print("test2: g_num: %d" % g_num)def main():t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

test2: g_num: 1042014
test1: g_num: 1080242
main: g_num: 1080242

以上的原因如下:

  • 假設:

    • t1代表線程1,t2代表線程2
    • g_num +=1 可分解成下面3個步驟:
      • 獲取 g_num的值, 記為t1.1(t2.1)
      • 將g_num的值加1, 記為t1.2(t2.2)
      • 將加1后的值存入g_num, 記為t1.3(t2.3)
  • 下面模擬執行步驟:(根據CPU的特性,分時執行)

    • 假設先執行t1.1
    • 再執行t1.2
    • 然后執行t2.1, 此時重新獲取g_num的值
    • 然后執行t1.3, 此時g_num的值并未改變

1.7 同步

以上問題可以通過線程同步來解決,在此之前,需要先了解互斥鎖:

  • 當多個線程幾乎同時修改某一個共享數據的時候,需要進行同步控制
  • 互斥鎖未資源引入了一個狀態: 鎖定/非鎖定
  • 某個線程要更改共享數據的時候,先將其鎖定,此時資源的狀態為"鎖定",其他線程不能更改;知道該線程釋放資源,將資源的狀態變為"非鎖定",其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有一個線程進行寫入操作,從而保證了多線程情況下數據的正確性

下面是互斥鎖的基本使用:

# 創建鎖
mutex = threading.Lock()# 鎖定
mutex.acquire()# 釋放
mutex.release()

注意:

  • 如果這個鎖之前是沒有上鎖得,那么acquire不會堵塞
  • 如果在調用acquire之前已經被上鎖了,那么acquire將會被阻塞直至release釋放

具體做法如下:

import threading
import timedef test1(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test1: g_num: %d" % g_num)def test2(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test2: g_num: %d" % g_num)g_num = 0
metex = threading.Lock()
def main():# 創建互斥鎖t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

說明:

  • 全局變量中創建一個互斥鎖: metex = threading.Lock()
  • 在原子代碼前面添上: metex.acquire()
    • 原子代碼: 即要么一次性全部執行,要么不執行的不可分割的代碼
  • 在原子代碼后面添上: metex.release()

1.8 死鎖

如果兩個線程分別占用一部分資源,并且同時等待對方的資源,就會造成死鎖的現象。下面使用python實現一個簡單的死鎖程序:

import threading
import timeclass MyThread1(threading.Thread):def run(self):# 線程1 假設下面都是原子代碼mutexA.acquire()print(self.name + "do1 up")time.sleep(1)mutexB.acquire()print(self.name + "do1 down")mutexB.release()mutexA.release()class MyThread2(threading.Thread):def run(self):mutexB.acquire()print(self.name + "do2 up")time.sleep(1)mutexA.acquire()print(self.name + "do2 down")mutexA.release()mutexB.release()mutexA = threading.Lock()
mutexB = threading.Lock()def main():t1 = MyThread1()t2 = MyThread2()t1.start()t2.start()if __name__ == "__main__":main()

說明:

  • 進入線程1,將mutexA鎖定,然后休眠1秒
  • 進入線程2,將mutexB鎖定,然后休眠1秒
  • 之后同時在線程1和2中各自獲取mutexB,mutexA而進入相互等待階段,即死鎖。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/250218.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/250218.shtml
英文地址,請注明出處:http://en.pswp.cn/news/250218.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python 鏈接匯總

MNIST手寫識別 轉載于:https://www.cnblogs.com/bycnboy/p/9095199.html

17種常用的JS正則表達式 非負浮點數 非負正數

<input typetext idSYS_PAGE_JumpPage nameSYS_PAGE_JumpPage size3 maxlength5 οnkeyupthis.valuethis.value.replace(/[^1-9]\D*$/,"") οndragenter"return false" οnpaste"return !clipboardData.getData(text).match(/\D/)"" sty…

python --- 使用conda配置pytorch

使用Conda配置PyTorch 1. 添加channels 下載地址 $ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ $ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ $ conda config --add channels htt…

LDAP第三天 MySQL+LDAP 安裝

https://www.easysoft.com/applications/openldap/back-sql-odbc.html OpenLDAP 使用 SQLServer 和 Oracle 數據庫。 https://www.cnblogs.com/bigbrotherer/p/7251372.html          CentOS7安裝OpenLDAPMySQLPHPLDAPadmin 1.安裝和設置數據庫 在CentOS7下&…

Myeclipse連接Mysql數據庫時報錯:Error while performing database login with the pro driver:unable...

driver template: Mysql connector/j&#xff08;下拉框進行選擇&#xff09; driver name: 任意填&#xff0c;最好是數據庫名稱&#xff0c;方便查找 connection URL: jdbc:mysql://localhost:3306/programmableweb User name: 用戶名 password: 密碼 Driver jars: 添加jar包…

Centos6.5靜態IP設置

1.創建新的虛擬機 2.打開終端&#xff0c;打開/etc/sysconfig/network-scripts/ifcfg-eth0文件 3.將BOOTPROTOstatic&#xff0c;原值為dhcp 4.添加 IPADDR192.168.43.125  #靜態IP GATEWAY192.168.43.1  #網關 NETMASK255.255.255.0  #子網掩碼 NETWORK192.168.43.0  …

matlab --- 圖像處理基礎

MATLAB圖像處理 1. 數字圖像處理 參考 數字圖像處理(Digital Image Processing)又稱為計算機圖像處理,是一種將圖像信號數字化利用計算進行處理的過程。隨著計算機科學、電子學和光學的發展,數字圖像處理已經廣泛的應用到諸多領域之中。本小節主要介紹圖像的概念、分類和數字…

java 注解默認值

package com.zejian.annotationdemo;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/** * Created by wuzejian on 2017/5/19. * 數據類型使用Demo */T…

[python、flask] - POST請求

1. 微信小程序POST傳遞數據給flask服務器 小程序端 // 提交POST數據 import { request } from "../../request/index.js"async handleDetectionPoints() {let params {url: "/detect_points",data: {"points": arr,"img_name": thi…

[vue]data數據屬性及ref獲取dom

data項的定義 this.$refs獲取dom 獲取不到數據 這樣中轉下才ok 小結: data里不能用this.$ref. 另外使用visjs時候 view-source:http://visjs.org/examples/network/basicUsage.html 加載不出東西,點了按鈕觸發才ok 小結: create里應該是從上到下執行的. 轉載于:https://www.cnb…

Linux命令基礎3

1. 計劃任務&#xff1a;分為”一次性“ 和”長期性“ 一次性任務是由atq服務/進程來實現的&#xff0c;計劃的管理操作是at命令&#xff1a; at <時間> : 安排一次性任務 atq 或at -l &#xff1a; 查看任務列表 at -c 序號&#xff1a; 預覽任務與設置環境 atrm 序號…

[異步、tensorflow] - 子線程操作tensor,主線程處理tensor

參考整體流程如下圖 代碼 import tensorflow as tf"""模擬: 子線程不停的取數據放入隊列中, 主線程從隊列中取數據執行包含: 作用域的命名、把程序的圖結構寫入事件、多線程 """# 模擬異步存入樣本. # 1、 定義一個隊列,長度為1000 with tf.va…

Element

官網&#xff1a;http://element-cn.eleme.io/#/zh-CN 轉載于:https://www.cnblogs.com/weibanggang/p/9995433.html

ubuntu18.04下安裝Anaconda及numpy、matplotlib

為了學習深度學習&#xff0c;我需要首先掌握利用python進行科學計算的知識&#xff0c;順便復習一下線性代數、微積分、概率論。當然&#xff0c;現在我要做的是安裝Anaconda。 1、官網下載&#xff0c;linux版本&#xff1a;https://www.anaconda.com/download 2、如果太慢&a…

[tensorflow] - csv文件讀取

參考 文件流程 csv讀取流程 函數的流程 import tensorflow as tf import os"""tensorflow中csv文件的讀取1、 先找到文件,構造一個列表2、 構造一個文件隊列3、 讀取(read)隊列內容csv: 讀取一行二進制文件: 指定一個樣本的bytes讀取圖片文件: 按一張一張…

課程模塊表結構

課程模塊 我們要開始寫課程模塊了~~課程模塊都有哪些功能呢~~ 我們的課程模塊&#xff0c;包括了免費課程以及專題課程兩個方向~~ 主要是課程的展示&#xff0c;點擊課程進入課程詳細頁面~~ 課程詳細頁面展示&#xff0c;課程的概述&#xff0c;課程的價格策略&#xff0c;課程…

vue中computed、metfods、watch的區別

一、computed和methods 我們可以將同一函數定義為一個 method 或者一個計算屬性。對于最終的結果&#xff0c;兩種方式確實是相同的。 不同的是computed計算屬性是基于它們的依賴進行緩存的。計算屬性computed只有在它的相關依賴發生改變時才會重新求值。這就意味著只要計算依賴…

OI生涯回憶錄(二)

&#xff08;二&#xff09;NOIP2016之后到HLOI2017 之后變得有點頹廢&#xff0c;因為有的地方難度上來了&#xff0c;碰見不會的題我就會放挺。又或者有時候題水&#xff0c;改完了就不思進取了。到了過年前那幾天連著考了幾天試&#xff0c;好像是長春那邊冬令營&#xff08…

[tensorflow、神經網絡] - 使用tf和mnist訓練一個識別手寫數字模型,并測試

參考 包含: 1.層級的計算、2.訓練的整體流程、3.tensorboard畫圖、4.保存/使用模型、5.總體代碼(含詳細注釋) 1. 層級的計算 如上圖,mnist手寫數字識別的訓練集提供的圖片是 28 * 28 * 1的手寫圖像,初始識別的時候,并不知道一次要訓練多少個數據,因此輸入的規模為 [None, 784].…

面向過程、面向函數、面向對象的區別淺談

Python的面向過程、面向函數、面向對象的區別淺談 轉自--獵奇古今&#xff0c;加上其他 有人之前私信問我&#xff0c;python編程有面向過程、面向函數、面向對象三種&#xff0c;那么他們區別在哪呢&#xff1f; 面向過程就是將編程當成是做一件事&#xff0c;要按步驟完成&am…