《操作系統知識點整理》

目錄

  • 進程與線程比較
  • 多線程
    • 同步與互斥
    • 生產者與消費者
    • 哲學家就餐問題
    • 讀者寫者問題
  • 進程間通信
    • 管道
    • 消息隊列
    • 共享內存
    • 信號量
    • 信號
    • Socket
    • 互斥鎖與自旋鎖
    • 讀寫鎖
    • 樂觀鎖與悲觀鎖
    • 死鎖

進程與線程比較

  • 進程是資源(包括內存、打開的文件等)分配的單位,線程是 CPU 調度的單位;
  • 進程擁有一個完整的資源平臺,而線程只獨享必不可少的資源,如寄存器和棧;
  • 線程同樣具有就緒、阻塞、執行三種基本狀態,同樣具有狀態之間的轉換關系;
  • 線程能減少并發執行的時間和空間開銷;

線程相比進程能減少開銷,體現在:

  • 線程的創建時間比進程快,因為進程在創建的過程中,還需要資源管理信息,比如內存管理信息、文件管理信息,而線程在創建的過程中,不會涉及這些資源管理信息,而是共享它們;
  • 線程的終止時間比進程快,因為線程釋放的資源相比進程少很多;
  • 同一個進程內的線程切換比進程切換快,因為線程具有相同的地址空間(虛擬內存共享),這意味著同一個進程的線程都具有同一個頁表,那么在切換的時候不需要切換頁表。而對于進程之間的切換,切換的時候要把頁表給切換掉,而頁表的切換過程開銷是比較大的;
  • 由于同一進程的各線程間共享內存和文件資源,那么在線程之間數據傳遞的時候,就不需要經過內核了,這就使得線程之間的數據交互效率更高了

多線程

同步與互斥

多個線程如果競爭共享資源,如果不采取有效的措施,則會造成共享數據的混亂。

由于多線程執行操作共享變量的這段代碼可能會導致競爭狀態,因此我們將此段代碼稱為臨界區(*critical section*),它是訪問共享資源的代碼片段,一定不能給多線程同時執行。

我們希望這段代碼是互斥(mutualexclusion)的,也就說保證一個線程在臨界區執行時,其他線程應該被阻止進入臨界區

所謂同步,就是并發進程/線程在一些關鍵點上可能需要互相等待與互通消息,這種相互制約的等待與互通信息稱為進程/線程同步

  • :加鎖、解鎖操作;
  • 信號量:P、V 操作;

這兩個都可以方便地實現進程/線程互斥,而信號量比鎖的功能更強一些,它還可以方便地實現進程/線程同步。

鎖分為無等待鎖與自旋鎖:

當獲取不到鎖時,線程就會一直 wile 循環,不做任何事情,所以就被稱為「忙等待鎖」,也被稱為自旋鎖(*spin lock*)

無等待鎖顧明思議就是獲取不到鎖的時候,不用自旋。

既然不想自旋,那當沒獲取到鎖的時候,就把當前線程放入到鎖的等待隊列,然后執行調度程序,把 CPU 讓給其他線程執行。

信號量表示資源的數量,對應的變量是一個整型(sem)變量

  • P 操作:將 sem1,相減后,如果 sem < 0,則進程/線程進入阻塞等待,否則繼續,表明 P 操作可能會阻塞;
  • V 操作:將 sem1,相加后,如果 sem <= 0,喚醒一個等待中的進程/線程,表明 V 操作不會阻塞;

在這里插入圖片描述
在這里插入圖片描述

對于兩個并發線程,互斥信號量的值僅取 1、0 和 -1 三個值,分別表示:

如果互斥信號量為 1,表示沒有線程進入臨界區;
如果互斥信號量為 0,表示有一個線程進入臨界區;
如果互斥信號量為 -1,表示一個線程進入臨界區,另一個線程等待進入。
通過互斥信號量的方式,就能保證臨界區任何時刻只有一個線程在執行,就達到了互斥的效果。

生產者與消費者

  • 生產者在生成數據后,放在一個緩沖區中;
  • 消費者從緩沖區取出數據處理;
  • 任何時刻,只能有一個生產者或消費者可以訪問緩沖區;
  • 任何時刻只能有一個線程操作緩沖區,說明操作緩沖區是臨界代碼,需要互斥
  • 緩沖區空時,消費者必須等待生產者生成數據;緩沖區滿時,生產者必須等待消費者取出數據。說明生產者和消費者需要同步

我們需要三個信號量 :

互斥信號量 mutex:用于互斥訪問緩沖區,初始化值為 1

資源信號量 fullBuffers:用于消費者詢問緩沖區是否有數據,有數據則讀取數據,初始化值為 0(表明緩沖區一開始為空);

資源信號量 emptyBuffers:用于生產者詢問緩沖區是否有空位,有空位則生成數據,初始化值為 n (緩沖區大小);

在這里插入圖片描述

哲學家就餐問題

在這里插入圖片描述

  • 5 個老大哥哲學家,閑著沒事做,圍繞著一張圓桌吃面;
  • 巧就巧在,這個桌子只有 5 支叉子,每兩個哲學家之間放一支叉子;
  • 哲學家圍在一起先思考,思考中途餓了就會想進餐;
  • 奇葩的是,這些哲學家要兩支叉子才愿意吃面,也就是需要拿到左右兩邊的叉子才進餐
  • 吃完后,會把兩支叉子放回原處,繼續思考

那么問題來了,如何保證哲 學家們的動作有序進行,而不會出現有人永遠拿不到叉子呢?

讓偶數編號的哲學家「先拿左邊的叉子后拿右邊的叉子」,奇數編號的哲學家「先拿右邊的叉子后拿左邊的叉子」

在這里插入圖片描述

上面的程序,在 P 操作時,根據哲學家的編號不同,拿起左右兩邊叉子的順序不同。另外,V 操作是不需要分支的,因為 V 操作是不會阻塞的。

讀者寫者問題

讀者只會讀取數據,不會修改數據,而寫者即可以讀也可以修改數據。

不談讀優先于寫優先鎖,直接談公平讀寫鎖。

公平策略 :

開始來了一些讀者讀數據,它們全部進入讀者隊列,此時來了一個寫者,執行 P(falg) 操作,使得后續到來的讀者都阻塞在 flag 上,不能進入讀者隊列,這會使得讀者隊列逐漸為空,即 rCount 減為 0。

這個寫者也不能立馬開始寫(因為此時讀者隊列不為空),會阻塞在信號量 wDataMutex 上,讀者隊列中的讀者全部讀取結束后,最后一個讀者進程執行 V(wDataMutex),喚醒剛才的寫者,寫者則繼續開始進行寫操作。
為此時讀者隊列不為空),會阻塞在信號量 wDataMutex 上,讀者隊列中的讀者全部讀取結束后,最后一個讀者進程執行 V(wDataMutex),喚醒剛才的寫者,寫者則繼續開始進行寫操作。

進程間通信

每個進程的用戶地址空間都是獨立的,一般而言是不能互相訪問的,但內核空間是每個進程都共享的,所以進程之間要通信必須通過內核。

管道

管道傳輸數據是單向的,如果想相互通信,我們需要創建兩個管道才行。

匿名管道,用完了就銷毀。

命名管道,也被叫做 FIFO,因為數據是先進先出的傳輸方式

只有當管道里的數據被讀完后,命令才可以正常退出。

管道這種通信方式效率低,不適合進程間頻繁地交換數據。當然,它的好處,自然就是簡單,同時也我們很容易得知管道里的數據已經被另一個進程讀取了。

對于匿名管道,它的通信范圍是存在父子關系的進程。因為管道沒有實體,也就是沒有管道文件,只能通過 fork 來復制父進程 fd 文件描述符,來達到通信的目的。

對于命名管道,它可以在不相關的進程間也能相互通信。因為命令管道,提前創建了一個類型為管道的設備文件,在進程里只要使用這個設備文件,就可以相互通信。

消息隊列

A 進程要給 B 進程發送消息,A 進程把數據放在對應的消息隊列后就可以正常返回了,B 進程需要的時候再去讀取數據就可以了。

消息隊列是保存在內核中的消息鏈表,在發送數據時,會分成一個一個獨立的數據單元,也就是消息體(數據塊),消息體是用戶自定義的數據類型,消息的發送方和接收方要約定好消息體的數據類型,所以每個消息體都是固定大小的存儲塊,不像管道是無格式的字節流數據。如果進程從消息隊列中讀取了消息體,內核就會把這個消息體刪除。
消息隊列不適合比較大數據的傳輸,因為在內核中每個消息體都有一個最大長度的限制,同時所有隊列所包含的全部消息體的總長度也是有上限。

消息隊列通信過程中,存在用戶態與內核態之間的數據拷貝開銷,因為進程寫入數據到內核中的消息隊列時,會發生從用戶態拷貝數據到內核態的過程,同理另一進程讀取內核中的消息數據時,會發生從內核態拷貝數據到用戶態的過程。

共享內存

共享內存的機制,就是拿出一塊虛擬地址空間來,映射到相同的物理內存中

這樣這個進程寫入的東西,另外一個進程馬上就能看到了,都不需要拷貝來拷貝去,傳來傳去,大大提高了進程間通信的速度。

信號量

了防止多進程競爭共享資源,而造成的數據錯亂,所以需要保護機制,使得共享的資源,在任意時刻只能被一個進程訪問。正好,信號量就實現了這一保護機制。

信號量其實是一個整型的計數器,主要用于實現進程間的互斥與同步,而不是用于緩存進程間通信的數據

控制信號量的方式有兩種原子操作 :

一個是 P 操作,這個操作會把信號量減去 -1,相減后如果信號量 < 0,則表明資源已被占用,進程需阻塞等待;相減后如果信號量 >= 0,則表明還有資源可使用,進程可正常繼續執行。
另一個是 V 操作,這個操作會把信號量加上 1,相加后如果信號量 <= 0,則表明當前有阻塞中的進程,于是會將該進程喚醒運行;相加后如果信號量 > 0,則表明當前沒有阻塞中的進程;

P 操作是用在進入共享資源之前,V 操作是用在離開共享資源之后,這兩個操作是必須成對出現的 。

兩個進程互斥訪問共享內存,我們可以初始化信號量為 1

在這里插入圖片描述

進程 A 在訪問共享內存前,先執行了 P 操作,由于信號量的初始值為 1,故在進程 A 執行 P 操作后信號量變為 0,表示共享資源可用,于是進程 A 就可以訪問共享內存。
若此時,進程 B 也想訪問共享內存,執行了 P 操作,結果信號量變為了 -1,這就意味著臨界資源已被占用,因此進程 B 被阻塞。
直到進程 A 訪問完共享內存,才會執行 V 操作,使得信號量恢復為 0,接著就會喚醒阻塞中的線程 B,使得進程 B 可以訪問共享內存,最后完成共享內存的訪問后,執行 V 操作,使信號量恢復到初始值 1。
信號初始化為 1,就代表著是互斥信號量,它可以保證共享內存在任何時刻只有一個進程在訪問,這就很好的保護了共享內存。

進程 A 是負責生產數據,而進程 B 是負責讀取數據,這兩個進程是相互合作、相互依賴的,進程 A 必須先生產了數據,進程 B 才能讀取到數據,所以執行是有前后順序的。

信號量來實現多進程同步的方式,我們可以初始化信號量為 0

在這里插入圖片描述

如果進程 B 比進程 A 先執行了,那么執行到 P 操作時,由于信號量初始值為 0,故信號量會變為 -1,表示進程 A 還沒生產數據,于是進程 B 就阻塞等待;
接著,當進程 A 生產完數據后,執行了 V 操作,就會使得信號量變為 0,于是就會喚醒阻塞在 P 操作的進程 B;
最后,進程 B 被喚醒后,意味著進程 A 已經生產了數據,于是進程 B 就可以正常讀取數據了。

信號

對于異常情況下的工作模式,就需要用「信號」的方式來通知進程。

信號是進程間通信機制中唯一的異步通信機制,因為可以在任何時候發送信號給某一進程,一旦有信號產生,我們就有下面這幾種,用戶進程對信號的處理方式。

用戶進程對信號的處理方式 :

1.執行默認操作

2.捕捉信號。 為信號定義一個信號處理函數。當信號發生時,我們就執行相應的信號處理函數

3.忽略信號

Socket

跨網絡與不同主機上的進程之間通信,就需要 Socket 通信了。

互斥鎖與自旋鎖

加鎖的目的就是保證共享資源在任意時間里,只有一個線程訪問,這樣就可以避免多線程導致共享數據錯亂的問題。

  • 互斥鎖加鎖失敗后,線程會釋放 CPU ,給其他線程;

  • 自旋鎖加鎖失敗后,線程會忙等待,直到它拿到鎖;

互斥鎖是一種「獨占鎖」,比如當線程 A 加鎖成功后,此時互斥鎖已經被線程 A 獨占了,只要線程 A 沒有釋放手中的鎖,線程 B 加鎖就會失敗,于是就會釋放 CPU 讓給其他線程,既然線程 B 釋放掉了 CPU,自然線程 B 加鎖的代碼就會被阻塞

在這里插入圖片描述

互斥鎖加鎖失敗時,會從用戶態陷入到內核態,讓內核幫我們切換線程,雖然簡化了使用鎖的難度,但是存在一定的性能開銷成本。 會有兩次線程上下文切換的成本

如果你能確定被鎖住的代碼執行時間很短,就不應該用互斥鎖,而應該選用自旋鎖,否則使用互斥鎖。

自旋鎖 在「用戶態」完成加鎖和解鎖操作,不會主動產生線程上下文切換,所以相比互斥鎖來說,會快一些,開銷也小一些 。

使用自旋鎖的時候,當發生多線程競爭鎖的情況,加鎖失敗的線程會「忙等待」,直到它拿到鎖。

自旋鎖是最比較簡單的一種鎖,一直自旋,利用 CPU 周期,直到鎖可用。需要注意,在單核 CPU 上,需要搶占式的調度器(即不斷通過時鐘中斷一個線程,運行其他線程)。否則,自旋鎖在單 CPU 上無法使用,因為一個自旋的線程永遠不會放棄 CPU。

讀寫鎖

讀寫鎖適用于能明確區分讀操作和寫操作的場景

讀寫鎖從字面意思我們也可以知道,它由「讀鎖」和「寫鎖」兩部分構成,如果只讀取共享資源用「讀鎖」加鎖,如果要修改共享資源則用「寫鎖」加鎖。

讀寫鎖的工作原理是:

當「寫鎖」沒有被線程持有時,多個線程能夠并發地持有讀鎖。
但是,一旦「寫鎖」被線程持有后,讀線程的獲取讀鎖的操作會被阻塞,而且其他寫線程的獲取寫鎖的操作也會被阻塞。
讀寫鎖在讀多寫少的場景,能發揮出優勢

讀寫鎖可以分為「讀優先鎖」和「寫優先鎖」。

「讀優先鎖」

當讀線程 A 先持有了讀鎖,寫線程 B 在獲取寫鎖的時候,會被阻塞,并且在阻塞過程中,后續來的讀線程 C 仍然可以成功獲取讀鎖,最后直到讀線程 A 和 C 釋放讀鎖后,寫線程 B 才可以成功獲取寫鎖。

「寫優先鎖」

當讀線程 A 先持有了讀鎖,寫線程 B 在獲取寫鎖的時候,會被阻塞,并且在阻塞過程中,后續來的讀線程 C 獲取讀鎖時會失敗,于是讀線程 C 將被阻塞在獲取讀鎖的操作,這樣只要讀線程 A 釋放讀鎖后,寫線程 B 就可以成功獲取讀鎖。

「公平讀寫鎖」

公平讀寫鎖比較簡單的一種方式是:用隊列把獲取鎖的線程排隊,不管是寫線程還是讀線程都按照先進先出的原則加鎖即可,這樣讀線程仍然可以并發,也不會出現「饑餓」的現象。

樂觀鎖與悲觀鎖

互斥鎖、自旋鎖、讀寫鎖,都是屬于悲觀鎖 。

悲觀鎖做事比較悲觀,它認為多線程同時修改共享資源的概率比較高,于是很容易出現沖突,所以訪問共享資源前,先要上鎖

那相反的,如果多線程同時修改共享資源的概率比較低,就可以采用樂觀鎖。

樂觀鎖做事比較樂觀,它假定沖突的概率很低,它的工作方式是:先修改完共享資源,再驗證這段時間內有沒有發生沖突,如果沒有其他線程在修改資源,那么操作完成,如果發現有其他線程已經修改過這個資源,就放棄本次操作

樂觀鎖全程并沒有加鎖,所以它也叫無鎖編程

只有在沖突概率非常低,且加鎖成本非常高的場景時,才考慮使用樂觀鎖。

死鎖

當線程A持有一個資源A,正在等待獲取資源B;而線程B持有資源B,卻在等待獲取資源A,死鎖就產生了。

產生條件:

  • 互斥:多個線程對于需要的資源進行互斥的訪問(生產者和消費者競爭一個mutex鎖)
  • 非搶占:線程在競爭到資源后,不能被搶占(消費者搶到mutex,迫使生產者等待)
  • 持有并等待:線程在競爭中持有了資源,同時有在等待其他資源。(消費者獲得mutex鎖后,等待生產者的sem_post(&not_empty))
  • 環路等待:線程之間存在一個環路,環路上每個線程都負責控制一個資源,但這個資源又是下一個線程所要申請的。

方法一 . 破壞互斥條件

如果我們能在兩個線程跑之前,能給每個線程單獨拷貝一份資源AB的副本,就能有效的避免死鎖了 。

方法二. 破壞環路等待條件

可以強制規定任何線程取資源的順序只能是 “先取A再取B”的話,就能避免死鎖了 .

方法三.破壞不剝奪條件

除非線程自己還資源,否則線程會一直占有資源,是形成不可剝奪條件的原因 。 可以通過設置一個”最長占用時間“的閾值來解決這個問題 。如果過了10分鐘仍然沒有進入下一個步驟,則歸還已有的資源。

方法四.破壞請求和保持條件

這里我們可以通過規定在任何情況下,一個線程獲取一個資源之后,必須歸還了資源之后才能請求另一個資源,就可以有效解決這個問題。

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

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

相關文章

for,foreach,iterator的用法和區別

相同點&#xff1a; 三個都可以用來遍歷數組和集合不同點&#xff1a;1.形式差別 for的形式是 for&#xff08;int i0;i<arr.size();i&#xff09;{...} foreach的形式是 for&#xff08;int i&…

和菜鳥一起學linux總線驅動之初識spi驅動主要結構

既然知道了協議了&#xff0c;那么就可以開始去瞧瞧linux kenerl中的spi的驅動代碼了&#xff0c;代碼中有很多的結構體&#xff0c;還是對主要的結構體先做個了解吧&#xff0c;那樣才可以很好的理解驅動。主要是include/linux/spi.h 首先是SPI的主機和從機通信接口&#xff0…

操作系統大內核和微內核_操作系統中的內核

操作系統大內核和微內核A Kernel is the central component of an Operating System. The Kernel is also said to be the heart of the Operating System. It is responsible for managing all the processes, memory, files, etc. The Kernel functions at the lowest level …

《MySQL——鎖》

全局鎖是什么&#xff1f;全局鎖有什么用&#xff1f;全局鎖怎么用&#xff1f; 全局鎖主要用在邏輯備份過程中&#xff0c;對于InnoDB 引擎的庫&#xff0c;使用–single-transaction; MySQL 提供了一個加全局讀鎖的方法&#xff0c;命令是 Flush tables with read lock (FTW…

搜索引擎Constellio及Google Search Appliances connectors

做搜索產品的時候發現國外一個同類型的產品contellio&#xff0c;發現功能比較強大&#xff0c;先記錄下來 貌似可以添加文檔 網站 以及數據庫等不同類型的數據源 http://wiki.constellio.com/index.php/Main_Page http://www.constellio.com/ http://www.constellio.com htt…

dig下載_DIG的完整形式是什么?

dig下載DIG&#xff1a;副監察長 (DIG: Deputy Inspector General) DIG is an abbreviation of the Deputy Inspector General. It is a high-level position in the Indian Police Service. The officers who already offered service on Senior Superintendent of Police (SS…

分類器是如何做檢測的?——CascadeClassifier中的detectMultiScale函數解讀

原地址&#xff1a;http://blog.csdn.net/delltdk/article/details/9186875 在進入detectMultiScal函數之前&#xff0c;首先需要對CascadeClassifier做初始化。 1. 初始化——read函數 CascadeClassifier的初始化很簡單&#xff1a; cv::CascadeClassifier classifier; cl…

<MySQL>何時使用普通索引,何時使用唯一索引

如果能夠保證業務代碼不會寫入重復數據&#xff0c;就可以繼續往下看。 如果業務不能保證&#xff0c;那么必須創建唯一索引。 關于查詢能力 普通索引和唯一索引在查詢能力上是沒有很大差別的。 如&#xff1a;select id from T where k5 1、普通索引查找到滿足條件的第一個記…

Web版OutLook,利用POP接收郵件服務器郵件

一直想做一個Web版的OutLook&#xff0c;所以才萌生這個想法&#xff0c;其實以前也接觸過這方面的東西。于是上網找了找&#xff0c;漫天的都是Jmail來接收&#xff0c;好吧&#xff0c;既然大家都在用我也就下載下來試試了。 什么&#xff0c;怎么總是報錯呢&#xff1f;原來…

abs std::abs_ABS的完整形式是什么?

abs std::absABS&#xff1a;防抱死制動系統 (ABS: Anti-lock Braking System) ABS is an abbreviation of the Anti-lock Braking System. It is a safety anti-skid braking system that is used on a variety of aircraft, automobiles and other land vehicles, such as mo…

ubuntu 使用

shell 命令歷史搜索 &#xff1a; ctrl r使能 session 選擇界面&#xff1a;安裝gnome-session-fallback安裝lwqq轉載于:https://www.cnblogs.com/JonnyLulu/p/3600263.html

漢字速查使用方法簡介

《漢字速查》&#xff08;HanziSearcher&#xff09;是一個支持全漢字字典和詞典的檢索工具。其界面如下所示。 界面上方為工具欄。 左方為字典和詞典檢索欄。 右方在啟動時顯示版權信息和作者的聯系方式&#xff0c;在執行檢索時&#xff0c;顯示檢索結果。 檢索方法 漢字速查…

android jni示例_Android服務示例

android jni示例A service is a component that runs in the background for supporting different types of operations that are long running. The user is not interacted with these. These perform task even if application is destroyed. Examples include handling of…

《MySQL——選錯索引,該如何做》

如果不斷地刪除歷史數據和新增數據&#xff0c;MySQL有時會選錯索引。 選擇索引是優化器的工作&#xff0c;優化器優化時會考慮的因素&#xff1a;掃描行數、是否需要排序、是否使用臨時表 MySQL通過統計索引上的基數&#xff0c;作為索引的區分度。 統計方法時采樣統計&#x…

LPWSTR 類型的實參與const.char *類型形參不兼容

CString csPlus; CString csSummand; m_PlusNumber.GetWindowTextW(csPlus); m_Summand.GetWindowTextW(csSummand); int nPlus atoi(csPlus.GetBuffer(0)); //將編輯框文本轉換成整數// int nPlus atoi(strcpy(csPlus.GetBuffer(10),"aa")); csPlus.ReleaseBu…

空間換時間,把遞歸的時間復雜度降低到O(2n)

遞歸算法的時間復雜度除非只有前兩項&#xff0c;否則都不是線性的&#xff0c;并且相當耗費內存。我們用最常見的的fibonacci數列來說明&#xff1a; function fibonacci(n){if( n 0 || n 1){return n;} else {return fibonacci(n - 1) fibonacci(n - 2);} } 這是一種最常見…

scala char_Scala中的Char數據類型

scala charScala Char數據類型 (Scala Char Data Type) Character (char) in Scala is a data type that is equivalent to 16-bit unsigned integer. The character data type stores a single character. It can be an alphabet, numbers, symbols, etc. The character takes…

《MySQL——給長字符串加索引》

對于長字符串&#xff0c;可用如下方式建立索引&#xff1a; &#xff08;1&#xff09;前綴索引 &#xff08;2&#xff09;字符串倒敘前綴索引 &#xff08;3&#xff09;添加hash字段并在hash字段上加索引 &#xff08;4&#xff09;字段拆分&#xff08;一個字段可拆分為兩…

[藍橋杯歷屆試題] 歐拉與雞蛋

大數學家歐拉在集市上遇到了本村的兩個農婦&#xff0c;每人跨著個空籃子。她們和歐拉打招呼說兩人剛剛賣完了所有的雞蛋。 歐拉隨便問&#xff1a;“賣了多少雞蛋呢&#xff1f;” 不料一個說&#xff1a;“我們兩人自己賣自己的&#xff0c;一共賣了150個雞蛋&#xff0c;雖然…

Python元組練習

Here, we are covering following Python tuple exercises, 在這里&#xff0c;我們將介紹以下Python元組練習 &#xff0c; Creating & printing a tuple 創建和打印元組 Unpacking the tuple into strings 將元組解包成字符串 Create a tuple containing the letters of…