Lua | 每日一練 (4)

💢歡迎來到張胤塵的技術站
💥技術如江河,匯聚眾志成。代碼似星辰,照亮行征程。開源精神長,傳承永不忘。攜手共前行,未來更輝煌💥

文章目錄

  • Lua | 每日一練 (4)
    • 題目
    • 參考答案
      • 線程和協程
        • 調度方式
        • 上下文切換
        • 資源占用
        • 實現機制
        • 使用場景
      • `lua` 中的協程
        • 協程的生命周期
        • 主要函數
          • 創建協程
          • 啟動或恢復協程
          • 檢查當前是否在主協程中運行
          • 暫停協程
          • 檢測協程是否可暫停
          • 獲取協程狀態
          • 包裝函數
          • 關閉協程
        • 具體使用

Lua | 每日一練 (4)

題目

協程和線程有何區別?簡述 lua 中的協程。

參考答案

線程和協程

協程和線程雖然在某些方面有相似之處,但它們在設計目標、實現原理和使用方式上有很大的區別。下面從調用方式、上下文切換、資源使用、實現機制、使用場景這幾個方面進行闡述。

調度方式
  • 線程:線程的執行由操作系統內核控制,操作系統會根據調度算法(如時間片輪轉、優先級調度等)自動切換線程的執行。另外,線程的切換時間點不可預測,程序無法直接控制線程的暫停和恢復。
  • 協程:協程的執行由程序顯式控制,需要開發者通過 coroutine.yieldcoroutine.resume 顯式地暫停和恢復協程。協程的切換完全由程序邏輯決定,切換點是明確的。
上下文切換
  • 線程:線程切換涉及操作系統內核的上下文切換,需要保存和恢復線程的寄存器狀態、棧信息等,開銷較大。
  • 協程:協程的上下文切換在用戶態完成,不需要操作系統內核介入,開銷非常小。
資源占用
  • 線程:每個線程都有自己的棧空間,通常默認分配 8 MB,資源占用較大。如果線程數量過多會導致系統資源耗盡。
$ ulimit -s
8192
  • 協程:協程的棧空間是動態分配的,通常占用較少的內存。另外,協程的數量可以非常大,適合處理大規模的并發任務。
實現機制
  • 線程:線程是操作系統提供的并發機制,由操作系統內核管理。線程的創建和銷毀需要系統調用,涉及內核態和用戶態的切換。
  • 協程:協程是語言層面的機制,由 lua 解釋器實現。協程的創建和切換完全在用戶態完成,不涉及操作系統內核。
使用場景
  • 線程:適合處理真正的并發任務,例如多核 CPU 上的并行計算,處理 I/O 密集型任務。
  • 協程:適合處理單核 CPU 上的并發任務,尤其是需要頻繁切換的場景;適合實現非阻塞 I/O 操作,例如網絡編程中的異步請求處理。

lua 中的協程

lua 中,協程是實現異步編程的核心工具之一。由于 lua 本身沒有內置的多線程支持,而協程提供了一種輕量級的并發機制,可以用來模擬異步操作,從而實現非阻塞的程序設計。

協程的生命周期

lua 協程的生命周期包括以下幾個階段:

  • 創建:使用 coroutine.create 創建一個協程。此時協程處于掛起狀態,尚未開始執行。
  • 運行:使用 coroutine.resume 啟動或恢復協程的執行。
  • 暫停:在協程執行過程中,可以通過 coroutine.yield 暫停協程的執行,將控制權交回主程序。
  • 結束:當協程運行完成或因錯誤終止時,協程進入結束狀態。

協程的狀態之間切換,如下圖所示:

在這里插入圖片描述

主要函數

下面介紹 lua 中關于協程的主要函數。

創建協程
local co = coroutine.create(f)
  • f:協程函數,表示協程的主體邏輯。
  • 返回一個協程對象 co,類型為 thread。協程對象可以用于后續的 coroutine.resumecoroutine.yield 等操作。

例如:

-- func 是協程函數,主體邏輯
local function func()print("Coroutine is running")
end-- 創建協程
local co = coroutine.create(func)
print(co) -- thread: 0x60f0c8666df8
啟動或恢復協程
ok, ... = coroutine.resume(co, ...)
  • co:要啟動或者恢復的協程對象。
  • ...:可選參數,這些參數會傳遞給協程中的 coroutine.yield 或協程的入口函數。
  • ok:布爾值,表示協程是否成功恢復。如果協程因錯誤終止,返回 false
  • ...:協程中 coroutine.yield 的返回值。如果協程運行完成,返回值為 nil

說明:

  • 如果協程處于 suspended 狀態,coroutine.resume 會啟動或恢復協程的執行。
  • 如果協程已經處于 running 狀態,調用 coroutine.resume 會拋出錯誤。
  • 如果協程已經處于 dead 狀態,調用 coroutine.resume 也會拋出錯誤。

例如:

local function func(x, y)print("Coroutine started with:", x, y) -- Coroutine started with:	10	20
endlocal co = coroutine.create(func)local ok, result = coroutine.resume(co, 10, 20)
print(ok, result) -- true	nil
檢查當前是否在主協程中運行
current_co, is_main = coroutine.running()
  • current_co:當前運行的協程對象。
  • is_main:布爾值,表示當前是否在主協程中運行,如果是主協程返回 true,否則返回 false

說明:

  • 用于檢查當前是否在主協程中運行,以及當前協程是否是主線程。

例如:

local function printCurrentCoroutine()local current_co, is_main = coroutine.running()print(current_co, is_main)
endprintCurrentCoroutine() -- thread: 0x61617e0492a8	truelocal co = coroutine.create(function()print("Printing from the coroutine") -- Printing from the coroutineprintCurrentCoroutine()              -- thread: 0x61617e04fec8	false
end)coroutine.resume(co) -- 啟動協程
暫停協程
... = coroutine.yield(...)
  • ...:可選參數,這些參數會傳遞給調用 coroutine.resume 的代碼。
  • 返回值是 coroutine.resume 調用時傳遞的參數。

說明:

  • coroutine.yield 用于暫停當前協程的執行,并將控制權返回給調用 coroutine.resume 的代碼。
  • 協程暫停后,可以通過再次調用 coroutine.resume 恢復執行。

例如:

local function func()print("Coroutine running")              -- Coroutine runninglocal value = coroutine.yield("Yielded value")print("Coroutine resumed with:", value) -- Coroutine resumed with:	Hello
endlocal co = coroutine.create(func)
local ok, result = coroutine.resume(co)
print(ok, result) -- true	Yielded valuelocal ok, result = coroutine.resume(co, "Hello")
print(ok, result) -- true	nil
檢測協程是否可暫停
is_yieldable = coroutine.isyieldable([co])
  • co:可選參數,表示要檢查的協程對象。默認為當前運行的協程。
  • is_yieldable:布爾值,表示協程是否可以暫停。

說明:

  • 如果協程不是主協程且不在非可暫停的 C 函數中,則返回 true
  • 主協程調用時返回 false
  • 協程處于 suspendeddead 狀態時 coroutine.isyieldable 仍然返回 true,因為協程對象本身仍然是有效的,并且在 lua 的語義中,dead 狀態的協程仍然可以被認為是可以暫停的(盡管它已經無法再被恢復)。

例如:

local co = coroutine.create(function()print(coroutine.isyieldable()) -- truecoroutine.yield()
end)coroutine.resume(co)             -- 啟動協程
print(coroutine.isyieldable(co)) -- true
print(coroutine.status(co))      -- suspended
coroutine.resume(co)
print(coroutine.status(co))      -- dead
print(coroutine.isyieldable(co)) -- true
print(coroutine.isyieldable())   -- false
獲取協程狀態
status = coroutine.status(co)
  • co:要檢查狀態的協程對象。
  • 返回一個字符串,表示協程的當前狀態:
    • `running:協程正在運行。
    • suspended:協程處于暫停狀態。
    • dead:協程已經運行完成或因錯誤終止。
    • normal:協程尚未啟動(初始狀態)。

例如:

local function func(co)local status = coroutine.status(co)print(status) -- runningcoroutine.yield("hello, world!")
endlocal co = coroutine.create(func)
local status = coroutine.status(co)
print(status) -- suspendedlocal ok, result = coroutine.resume(co, co)
print(ok, result) -- true	hello, world!local ok, result = coroutine.resume(co, co)
print(ok, result) -- true	nillocal status = coroutine.status(co)
print(status) -- dead
包裝函數
f = coroutine.wrap(f)
  • f:協程函數,表示協程的主體邏輯。
  • 返回一個包裝函數 f。調用這個包裝函數時,會自動啟動或恢復協程的執行。

說明:

  • coroutine.wrapcoroutine.createcoroutine.resume 的簡化版本。
  • 包裝函數的返回值是協程中 coroutine.yield 的參數。
  • 如果協程運行完成,包裝函數返回 nil;如果協程因錯誤終止,會拋出錯誤。

例如:

local wrapped = coroutine.wrap(function()print("Coroutine running")local value = coroutine.yield("Yielded value")print("Coroutine resumed with:", value)
end)local result = wrapped()
print(result)    -- Yielded valuewrapped("Hello") -- Coroutine resumed with:	Hello
關閉協程
ok, err = coroutine.close(co)
  • co:要關閉的協程對象,協程的狀態必須是 suspendeddead 狀態。

  • ok:布爾值,表示操作是否成功。

  • err:如果操作失敗,返回錯誤信息。

說明:

  • 關閉協程 co,將其狀態設置為 dead,并關閉協程中所有待關閉的變量。
  • 如果協程已經是 dead 狀態,調用 coroutine.close 會返回 true

例如:

local co = coroutine.create(function()coroutine.yield() -- 暫停協程
end)coroutine.resume(co)                -- 啟動協程
print(coroutine.status(co))         -- suspendedlocal ok, err = coroutine.close(co) -- 關閉協程
print(ok, err)                      -- true	nil
print(coroutine.status(co))         -- dead
具體使用

使用協程實現經典的生產者-消費者模型。協程的暫停和恢復特性非常適合這種場景,因為生產者和消費者可以分別在協程中運行,通過共享隊列進行通信。

一個簡單的生產者-消費者模型實現,如下所示:

-- 隊列實現
local Queue = {}function Queue:new()local obj = {}setmetatable(obj, self)self.__index = selfobj.list = {}return obj
endfunction Queue:put(item)table.insert(self.list, item)
endfunction Queue:get()if #self.list <= 0 thenreturn nilendlocal item = self.list[1]table.remove(self.list, 1)return item
endfunction Queue:is_empty()return #self.list == 0
end-- 生產者函數
local function producer(queue, count)for i = 1, count doprint("Produced:", i)queue:put(i)coroutine.yield()end
end-- 消費者函數
local function consumer(queue, count)for i = 1, count dowhile queue:is_empty() doprint("Waiting for item...")coroutine.yield()endlocal item = queue:get()print("Consumed:", item)end
endlocal queue = Queue:new()
local count = 100 -- 生產/消費數量-- 創建生產者和消費者協程
local co_producer = coroutine.create(function()producer(queue, count)
end)local co_consumer = coroutine.create(function()consumer(queue, count)
end)-- 同時運行生產者和消費者協程
while coroutine.status(co_producer) ~= "dead" or coroutine.status(co_consumer) ~= "dead" doif coroutine.status(co_producer) ~= "dead" thencoroutine.resume(co_producer)endif coroutine.status(co_consumer) ~= "dead" thencoroutine.resume(co_consumer)end
end

🌺🌺🌺撒花!

如果本文對你有幫助,就點關注或者留個👍
如果您有任何技術問題或者需要更多其他的內容,請隨時向我提問。

在這里插入圖片描述

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

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

相關文章

Fiji —— 基于 imageJ 的免費且開源的圖像處理軟件

文章目錄 一、Fiji —— 用于科學圖像處理和分析1.1、工具安裝&#xff08;免費&#xff09;1.2、源碼下載&#xff08;免費&#xff09; 二、功能詳解2.0、Fiji - ImageJ&#xff08;Web應用程序&#xff09;2.1、常用功能&#xff08;匯總&#xff09;2.2、Fiji - Plugins&am…

PyQT(PySide)的上下文菜單策略設置setContextMenuPolicy()

在 Qt 中&#xff0c;QWidget 類提供了幾種不同的上下文菜單策略&#xff0c;這些策略通過 Qt::ContextMenuPolicy 枚舉類型來定義&#xff0c;用于控制控件&#xff08;如按鈕、文本框等&#xff09;在用戶右鍵點擊時如何顯示上下文菜單。 以下是 Qt::ContextMenuPolicy 枚舉中…

快慢指針【等分鏈表、判斷鏈表中是否存在環】

一、等分鏈表&#xff1a;找到鏈表的中間節點 Java 實現 class ListNode {int val;ListNode next;ListNode(int val) {this.val val;this.next null;} }public class MiddleOfLinkedList {public ListNode findMiddleNode(ListNode head) {if (head null) {return null;}L…

系統架構設計師—計算機基礎篇—計算機網絡

文章目錄 網絡互聯模型網絡協議與標準應用層協議FTP協議TFTP協議 HTTP協議HTTPS協議 DHCP動態主機配置協議DNS協議迭代查詢遞歸查詢 傳輸層協議網絡層協議IPV4協議IPV6協議IPV6數據報的目的地址IPV4到IPV6的過渡技術 網絡設計分層設計接入層匯聚層核心層 網絡布線綜合布線系統工…

計算機基礎面試(操作系統)

操作系統 1. 什么是進程和線程&#xff1f;它們的核心區別是什么&#xff1f; 專業解答&#xff1a; 進程是操作系統分配資源的基本單位&#xff0c;擁有獨立的內存空間&#xff1b;線程是進程內的執行單元&#xff0c;共享同一進程的資源。區別在于&#xff1a;進程間資源隔離…

考研408數據結構線性表核心知識點與易錯點詳解(附真題示例與避坑指南)

一、線性表基礎概念 1.1 定義與分類 定義&#xff1a;線性表是由n&#xff08;n≥0&#xff09;個相同類型數據元素構成的有限序列&#xff0c;元素間呈線性關系。 分類&#xff1a; 順序表&#xff1a;元素按邏輯順序存儲在一段連續的物理空間中&#xff08;數組實現&…

【實戰 ES】實戰 Elasticsearch:快速上手與深度實踐-1.2.2倒排索引原理與分詞器(Analyzer)

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 1.2.2倒排索引原理與分詞器&#xff08;Analyzer&#xff09;1. 倒排索引&#xff1a;搜索引擎的基石1.1 正排索引 vs 倒排索引示例數據對比&#xff1a; 1.2 倒排索引核心結…

Springboot項目本地連接并操作MySQL數據庫

目錄 前提 準備工作 用cmd在本地創建數據庫、表&#xff1a; 1.創建springboot項目&#xff08;已有可跳過&#xff09; 2.編輯Mybatis配置 3.連接數據庫 4.創建模型類&#xff0c;用于與數據庫里的數據表相連 5.創建接口mapper&#xff0c;定義對數據庫的操作 6.創建…

《寶塔 Nginx SSL 端口管理實戰指南:域名解析、端口沖突與后端代理解析》

&#x1f4e2; Nginx & SSL 端口管理分析 1?? 域名解析與 SSL 申請失敗分析 在使用寶塔申請 www.mywebsite.test 的 SSL 證書時&#xff0c;遇到了解析失敗的問題。最初&#xff0c;我認為 www 只是一個附加的前綴&#xff0c;不屬于域名的關鍵部分&#xff0c;因此只為…

java和Springboot和vue開發的企業批量排班系統人臉識別考勤打卡系統

演示視頻&#xff1a; https://www.bilibili.com/video/BV1KU9iYsEBU/?spm_id_from888.80997.embed_other.whitelist&t52.095574&bvidBV1KU9iYsEBU 主要功能&#xff1a; 管理員管理員工&#xff0c;采集員工人臉特征值存入數據庫&#xff0c;可選擇多個員工批量排班…

DeepSeek學習規劃

DeepSeek是一個專注于深度學習和人工智能技術研究與應用的平臺&#xff0c;旨在通過系統化的學習和實踐&#xff0c;幫助用戶掌握深度學習領域的核心知識和技能。為了在DeepSeek平臺上高效學習&#xff0c;制定一個科學合理的學習規劃至關重要。以下是一個詳細的學習規劃&#…

打開 Windows Docker Desktop 出現 Docker Engine Stopped 問題

一、關聯文章: 1、Docker Desktop 安裝使用教程 2、家庭版 Windows 安裝 Docker 沒有 Hyper-V 問題 3、安裝 Windows Docker Desktop - WSL問題 二、問題解析 打開 Docker Desktop 出現問題,如下: Docker Engine Stopped : Docker引擎停止三、解決方法 1、檢查服務是否…

突破Ajax跨域困境,解鎖前端通信新姿勢

一、引言 在當今的 Web 開發領域&#xff0c;前后端分離的架構模式已經成為主流&#xff0c;它極大地提升了開發效率和項目的可維護性。在這種開發模式下&#xff0c;前端通過 Ajax 技術與后端進行數據交互&#xff0c;然而&#xff0c;跨域問題卻如影隨形&#xff0c;成為了開…

Mercury、LLaDA 擴散大語言模型

LLaDA 參考&#xff1a; https://github.com/ML-GSAI/LLaDA https://ml-gsai.github.io/LLaDA-demo/ 在線demo&#xff1a; https://huggingface.co/spaces/multimodalart/LLaDA Mercury 在線demo&#xff1a; https://chat.inceptionlabs.ai/ 速度很快生成

Rust~String、str、str、String、Box<str> 或 Box<str>

Rust語言圣經中定義 str Rust 語言類型大致分為兩種&#xff1a;基本類型和標準庫類型&#xff0c;前者由語言特性直接提供&#xff0c;后者在標準庫中定義 str 是唯一定義在 Rust 語言特性中的字符串&#xff0c;但也是幾乎不會用到的字符串類型 str 字符串是 DST 動態大小…

大數據SQL調優專題——底層調優

引入 上一篇我們提到了調優的常見切入點&#xff0c;核心就是通過數據產出情況發現問題&#xff0c;借助監控等手段收集信息排查瓶頸在哪&#xff0c;最后結合業務理解&#xff0c;等價重寫思路去解決問題。 在實際工作場景中&#xff0c;去保證數據鏈路產出SLA的時候&#x…

Hue 編譯異常:ImportError: cannot import name ‘six‘ from ‘urllib3.packages‘

個人博客地址&#xff1a;Hue 編譯異常&#xff1a;ImportError: cannot import name six from urllib3.packages | 一張假鈔的真實世界 在編譯Hue的時候出現錯誤信息如下&#xff1a; Running /home/zhangjc/ysten/git/ysten-hue/build/env/bin/hue makemigrations --noinpu…

計算機網絡——詳解TCP三握四揮

文章目錄 前言一、三次握手1.1 三次握手流程1.2 tcp為什么需要三次握手建立連接&#xff1f; 二、四次揮手2.1 四次揮手流程2.2 為什么是四次&#xff0c;不是三次&#xff1f;2.3 為什么要等待2msl&#xff1f;2.4 TCP的保活計時器 前言 TCP和UDP是計算機網絡結構中運輸層的兩…

# C# 中堆(Heap)與棧(Stack)的區別

在 C# 中&#xff0c;堆和棧是兩種不同的內存分配機制&#xff0c;它們在存儲位置、生命周期、性能和用途上存在顯著差異。理解堆和棧的區別對于優化代碼性能和內存管理至關重要。 1. 棧&#xff08;Stack&#xff09; 1.1 定義 棧是一種后進先出&#xff08;LIFO&#xff0…

如何把圖片或者圖片地址存到 MySQL 數據庫中以及如何將這些圖片數據通過 JSP 顯示在網頁中

如何優雅地管理圖片&#xff1a;從MySQL數據庫存儲到JSP展示的全流程解析 在互聯網時代&#xff0c;一張引人入勝的圖片往往能為網站帶來巨大的流量。而作為開發者的我們&#xff0c;如何高效地管理和展示這些圖片資源則成為了一項重要的技術挑戰。今天&#xff0c;我們就一起…