
您好,我是湘王,這是我的51CTO博客。值此新春佳節,我給您拜年啦~祝您在新的一年中所求皆所愿,所行皆坦途,展宏“兔”,有錢“兔”,多喜樂,常安寧!
軟件開發中,除了進程和線程,還有協程的概念。但是在搞清楚協程這個概念之前,需要明白什么是進程和線程。
(資料圖片)
進程一般是應用程序的啟動實例,進程擁有代碼和打開的文件資源、數據資源、獨立的內存空間,例如,獨立部署的jar包、運行的redis、mongodb程序等,都可以成為進程。
而線程從屬于進程,是應用程序的實際執行者,一個進程至少包含一個主線程,或者有更多的子線程,線程擁有自己的棧空間。
看起來,在同一時刻多個線程是同時執行的,也就是并發執行的,但由于若干個線程對應同一個CPU,所以同一個時刻其實只有一個線程是處于執行狀態的。在一個時間片內執行哪個線程是不確定的,只能控制線程的優先級,但真正的線程調度是由CPU決定的。
般情況下,各個線程都是比較獨立的,各自也有明確的生命周期,如初始化、可運行、運行中、阻塞、銷毀等不同的狀態。
如果線程之間需要協作或者通信,那么可以通過volatile關鍵字、wait/notify方法、JUC工具類,或者是消息隊列MQ實現。無論如何,線程都會涉及到線程鎖、狀態切換及上下文切換。
協程是一種比線程更加輕量級的存在,是「線程中的線程」。協程也擁有獨立的堆棧,獨立的局部變量,獨立的指令指針,同時又與其它協程共享全局變量和其它內容。
協程不被操作系統管理,而是完全由線程內部控制,由程序顯式的進行,需要多個程序彼此協作才能完成功能,這就是協程名字的由來。
協程不是進程也不是線程,它是通過特殊的函數來實現的——這個特殊的函數可以在某個地方「掛起」,之后可以重新在其他的地方繼續運行。
一個線程之內可有多個這樣特殊的函數,也就是有多個協程同時運行,但多個協程的運行只能是「串行」的——一個協程運行時,其他協程必須要掛起。同時還要明確:協程也是有切換的,但它的切換不是像進程或線程那樣由操作系統完成,而是由用戶完成的,所以切換效率比進程或線程高。或者可以打一個這樣的比方:協程類似于正在同步中的多線程,而在等待同一個線程鎖的幾個線程也類似于協程。
協程是LUA中引入的概念,由于OpenResty是對LUA的封裝,因此也自然就具備了協程特性。
具體來說,resume和yeild是Lua協程的核心。首次調用時,resume的參數會直接傳遞給協程函數。非首次調用時,resume的另一個參數會成為yield的返回值,而yield的參數,也會成為resume額外的返回值。
coroutine.create(function)會傳一個函數作為參數來創建協程,返回coroutine,當遇到resume時就喚醒函數調用。
而coroutine.yield()使正在執行的函數掛起,傳遞給yeild的參數會作為resume的額外返回值。
先來驗證coroutine.create和coroutine.resume。
修改Nginx配置文件,將原來的include lua.conf改為xiecheng.conf。
vi /usr/local/openresty/nginx/conf/nginx.conf
修改xiecheng.conf內容:
vi /usr/local/openresty/nginx/conf/xiecheng.conf
修改完成后重啟openresty:
/usr/local/openresty/nginx/sbin/nginx -s reload
訪問瀏覽器:
http://IP地址/xiecheng
可以看到三次結果都不同:這其實就是resume返回值的三種情況
其他的協程方法包括:
1、coroutine.isyieldable(),表示如果正在運行的協程可以掛起,則返回true(只有主協程(線程)和C函數是無法讓出的);
2、coroutine.running(),用來判斷當前執行的協程是不是主線程,如果是就返回true;
3、coroutine.status(function),返回表示協程狀態的字符串:
running:正在執行中的協程;
suspended:還未結束卻被掛起(調用了yeild或還沒開始運行)的協程;
normal:協程A resume協程B時,協程A所處的狀態就是normal;
dead:發生錯誤或正常終止的協程,如果這時候對它調用resume,將返回false和錯誤消息,就像剛才展示的那樣。
修改配置文件驗證語法:
vi /usr/local/openresty/nginx/conf/xiecheng.conf
修改完成后重啟openresty:
/usr/local/openresty/nginx/sbin/nginx -s reload
訪問瀏覽器:
http://IP地址/xiecheng3
協程時序圖:
通過以上語法,可以得到幾個關于OpenResty協程的知識:
1、所有的協程都是通過resume和yield協作的,這是協程的核心所在,可以說沒有resume和yield的協作,就沒有協程;
2、resume和yield都是由開發者控制的,除此之外,是不會有任何其他第三方系統干預的,但線程就不一樣;
3、函數從哪里掛起,恢復時就從哪里開始執行,關于這一點,可以嘗試在r1中的coroutine.yield("b")前后各加上一行ngx.print()語句來驗證。
這只是Lua協程的很小一部分,Lua中沒有線程和異步編程的概念,但對于并發執行卻提供了協程的方式,這確實有點奇怪。ngx_lua模塊對協程做了封裝,可以直接通過ngx.thread API來調用。其原理就是協程A在運行中發現自己忙,則把CPU使用權讓出來給協程B使用,最后協程A能從中斷位置繼續執行,本地還是CPU獨占的單線程。
節日期間,您仍然可以隨時咨詢技術、產品、運營和管理相關問題,請關注后留言。歡迎騷擾,不勝榮幸~再次祝您兔年吉祥,萬事勝意!