Python3 源碼閱讀-深入了解Python GIL

今日得到: 三人行,必有我師焉,擇其善者而從之,其不善者而改之。

現在已經是2020年了,而在2010年的時候,大佬David Beazley就做了講座講解Python GIL的設計相關問題,10年間相信也在不斷改善和優化,但是並沒有將GIL從CPython中移除,可想而知,GIL已經深入CPython,難以移除。就目前來看,工作中常用的還是協程,多線程來處理高併發的I/O密集型任務。CPU密集型的大型計算可以用其他語言來實現。

1. GIL

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) —– Global Interpreter Lock

為了防止多線程共享內存出現競態問題,設置的防止多線程併發執行機器碼的一個Mutex。

2. python32 之前-基於opcode數量的調度方式

在python3.2版本之前,定義了一個tick計數器,表示當前線程在釋放gil之前連續執行的多少個字節碼(實際上有部分執行較快的字節碼並不會被計入計數器)。如果當前的線程正在執行一個 CPU 密集型的任務, 它會在 tick 計數器到達 100 之後就釋放 gil, 給其他線程一個獲得 gil 的機會。

(圖片來自 Understanding the Python GIL(youtube))

以opcode個數為基準來計數,如果有些opcode代碼複雜耗時較長,一些耗時較短,會導致同樣的100個tick,一些線程的執行時間總是執行的比另一些長。是不公平的調度策略。

(圖片來自Understanding-the-python-gil)

如果當前的線程正在執行一個 IO密集型的 的任務, 你執行 sleep/recv/send(...etc) 這些會阻塞的系統調用時, 即使 tick 計數器的值還沒到 100, gil 也會被主動地釋放。至於下次該執行哪一個線程這個是操作系統層面的,線程調度算法優先級調度,開發者沒辦法控制。

在多核機器上, 如果兩個線程都在執行 CPU 密集型的任務, 操作系統有可能讓這兩個線程在不同的核心上運行, 也許會出現以下的情況, 當一個擁有了 gil 的線程在一個核心上執行 100 次 tick 的過程中, 在另一個核心上運行的線程頻繁的進行搶佔 gil, 搶佔失敗的循環, 導致 CPU 瞎忙影響性能。 如下圖:綠色部分表示該線程在運行,且在執行有用的計算,紅色部分為線程被調度喚醒,但是無法獲取GIL導致無法進行有效運算等待的時間。

由圖可見,GIL的存在導致多線程無法很好的利用多核CPU的併發處理能力。

3. python3.2 之後-基於時間片的切換

由於在多核機器下可能導致性能下降, gil的實現在python3.2之後做了一些優化 。python在初始化解釋器的時候就會初始化一個gil,並設置一個DEFAULT_INTERVAL=5000, 單位是微妙,即0.005秒(在 C 裏面是用 微秒 為單位存儲, 在 python 解釋器中以秒來表示)這個間隔就是GIL切換的標誌。

// Python\ceval_gil.h
#define DEFAULT_INTERVAL 5000

static void _gil_initialize(struct _gil_runtime_state *gil)
{
    _Py_atomic_int uninitialized = {-1};
    gil->locked = uninitialized;
    gil->interval = DEFAULT_INTERVAL;
}

python中查看gil切換的時間

In [7]: import sys
In [8]: sys.getswitchinterval()
Out[8]: 0.005

如果當前有不止一個線程, 當前等待 gil 的線程在超過一定時間的等待后, 會把全局變量 gil_drop_request 的值設置為 1, 之後繼續等待相同的時間, 這時擁有 gil 的線程看到了 gil_drop_request 變為 1, 就會主動釋放 gil 並通過 condition variable 通知到在等待中的線程, 第一個被喚醒的等待中的線程會搶到 gil 並執行相應的任務, 將gil_drop_request設置為1的線程不一定能搶到gil

4 condition variable相關字段

  1. locked : locked 的類型是_Py_atomic_int, 值-1表示還未初始化,0表示當前的gil處於釋放狀態,1表示某個線程已經佔用了gil,這個值的類型設置為原子類型之後在 ceval.c 就可以不加鎖的對這個值進行讀取。
  2. interval:是線程在設置gil_drop_request這個變量之前需要等待的時長,默認是5000毫秒
  3. last_holder:存放了最後一個持有 gil 的線程的 C 中對應的 PyThreadState 結構的指針地址, 通過這個值我們可以知道當前線程釋放了 gil 后, 是否有其他線程獲得了 gil(可以採取措施避免被自己重新獲得)
  4. switch_number: 是一個計數器, 表示從解釋器運行到現在, gil 總共被釋放獲得多少次
  5. mutex:是一把互斥鎖, 用來保護 locked, last_holder, switch_number 還有 _gil_runtime_state 中的其他變量
  6. cond:是一個 condition variable, 和 mutex 結合起來一起使用, 當前線程釋放 gil 時用來給其他等待中的線程發送信號
  7. ** switch_cond and switch_mutex**

switch_cond 是另一個 condition variable, 和 switch_mutex 結合起來可以用來保證釋放后重新獲得 gil 的線程不是同一個前面釋放 gil 的線程, 避免 gil 切換時線程未切換浪費 cpu 時間

這個功能如果編譯時未定義 FORCE_SWITCHING 則不開啟

static void
drop_gil(struct _ceval_runtime_state *ceval, PyThreadState *tstate)
{
    ...

#ifdef FORCE_SWITCHING
    if (_Py_atomic_load_relaxed(&ceval->gil_drop_request) && tstate != NULL) {
        MUTEX_LOCK(gil->switch_mutex);
        /* Not switched yet => wait */
        if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
        {   
            /* 如果 last_holder 是當前線程, 釋放 switch_mutex 這把互斥鎖, 等待 switch_cond 這個條件變量的信號 */
            RESET_GIL_DROP_REQUEST(ceval);
            /* NOTE: if COND_WAIT does not atomically start waiting when
               releasing the mutex, another thread can run through, take
               the GIL and drop it again, and reset the condition
               before we even had a chance to wait for it. */
            /* 注意, 如果 COND_WAIT 不在互斥鎖釋放后原子的啟動,
                另一個線程有可能會在這中間拿到 gil 並釋放,
            '並且重置這個條件變量, 這個過程發生在了 COND_WAIT 之前 */
            COND_WAIT(gil->switch_cond, gil->switch_mutex);
        }
        MUTEX_UNLOCK(gil->switch_mutex);
    }
#endif
}

4. gil在main_loop中的體現

//
main_loop:
for (;;) {
    /* 如果 gil_drop_request 被其他線程設置為 1 */
    /* 給其他線程一個獲得 gil 的機會 */
    if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
    /* Give another thread a chance */
    if (_PyThreadState_Swap(&runtime->gilstate, NULL) != tstate) {
        Py_FatalError("ceval: tstate mix-up");
    }
    drop_gil(ceval, tstate);

    /* Other threads may run now */

    take_gil(ceval, tstate);

    /* Check if we should make a quick exit. */
    exit_thread_if_finalizing(runtime, tstate);

    if (_PyThreadState_Swap(&runtime->gilstate, tstate) != NULL) {
        Py_FatalError("ceval: orphan tstate");
        }
    }
    /* Check for asynchronous exceptions. */
    /* 忽略 */
    fast_next_opcode:
    switch (opcode) {
        case TARGET(NOP): {
            FAST_DISPATCH();
        }
        /* 忽略 */
        case TARGET(UNARY_POSITIVE): {
            PyObject *value = TOP();
            PyObject *res = PyNumber_Positive(value);
            Py_DECREF(value);
            SET_TOP(res);
            if (res == NULL)
                goto error;
            DISPATCH();
        }
    	/* 忽略 */
    }
    /* 忽略 */
}

這個很大的 for loop 會按順序逐個的加載 opcode, 並委派給中間很大的 switch statement 去進行執行, switch statement 會根據不同的 opcode 跳轉到不同的位置執行

for loop在開始位置會檢查 gil_drop_request變量, 必要的時候會釋放 gil

不是所有的 opcode 執行之前都會檢查 gil_drop_request 的, 有一些 opcode 結束時的代碼為 FAST_DISPATCH(), 這部分 opcode 會直接跳轉到下一個 opcode 對應的代碼的部分進行執行

而另一些 DISPATCH() 結尾的作用和 continue 類似, 會跳轉到 for loop 頂端, 重新檢測 gil_drop_request, 必要時釋放 gil

5 如何解決GIL

GIL只會對CPU密集型的程序產生影響,規避GIL限制主要有兩種常用策略:一是使用多進程,二是使用C語言擴展,把計算密集型的任務轉移到C語言中,使其獨立於Python,在C代碼中釋放GIL。當然也可以使用其他語言編譯的解釋器如 JpythonPyPy

6.總結

  1. Python語言和GIL沒有半毛錢關係,僅僅是由於歷史原因在CPython解釋器中難以移除GIL
  2. GIL:全局解釋器鎖,每個線程在執行的過程都需要先獲取GIL,確保同一時刻僅有一個線程執行代碼,所以python的線程無法利用多核。
  3. 線程在I/O操作等可能引起阻塞的system call之前,可以暫時釋放GIL,執行完畢后重新獲取GIL,python3.2以後使用時間片來切換線程,時間閾值是0.005秒,而python3.2之前是使用opcode執行的數量(tick=100)來切換的。
  4. Python的多線程在多核CPU上,只對於IO密集型計算產生正面效果;而當有至少有一個CPU密集型線程存在,那麼多線程效率會由於GIL而大幅下降

參考

Cpython-gil講解-zpoint

Python的GIL是什麼鬼-盧鈞軼(cenalulu)

Youtube-Understanding the Python GIL

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

武漢肺炎讓日本人想起311 福島媽媽陷憂鬱:再一次面對「看不見的敵人」

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

核災和武肺雙重考驗 福島人的「保養營隊」因疫情被迫中斷

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

法國費森翰核電廠吹熄燈號 將關閉第2反應爐

摘錄自2020年06月28日中央通訊社法國報導

法國費森翰(Fessenheim)核電廠在運轉40多年後,預定本月30日正式關閉。長期警告核污染的環保人士對費森翰除役自是大表歡迎,但另方面電廠關門也引發當地的經濟問題。

費森翰是法國最老的核電廠,於1977年開始運轉,原定屆40年除役,目前已逾3年。福島核災發生數月後,時任總統歐蘭德(Francois Hollande)承諾要關閉費森翰核電廠,但直到2018年,現任總統馬克宏(Emmanuel Macron)才批准費森翰除役。

費森翰核電廠由法國電力集團(EDF)經營,設有兩座反應爐,在今年2月已先停掉一部。第2座反應爐預定6月30日一早停爐卸載,但得花費數月時間,反應爐才足以冷卻下來,以移除核廢料,此一過程應在2023年前完成,但整廠全部拆卸最快也要到2040年。

能源議題
能源轉型
國際新聞
法國
核電廠
核能

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

日本7/1塑膠袋收費新制上路 業者推付費紙袋

摘錄自2020年06月28日中央通訊社日本報導

配合日本全國將從7月1日起實施塑膠購物袋收費新制,日本百貨公司等業者也將實施紙袋收費制度,希望促使消費者自行準備可重複利用的環保購物袋。

日本放送協會(NHK)報導,知名百貨業者表示,7月1日起將依序在直營的食品賣場取消提供塑膠製購物袋,取而代之是販售收費紙袋。收費紙袋較以往免費紙袋來得厚,提高耐用度,外層也做防潑水處理,每一個售價根據大小分成30日圓(約新台幣8元)跟50日圓。至於服裝及雜貨賣場,將繼續提供免費紙袋。

百貨業者表示,希望透過這樣的努力,可以提高消費者自配環保購物袋到三成左右。另外一家食品零售業者的450多家分店,已從4月起不再提供免費購物袋,取而代之的是販售每個15日圓的紙袋等,呼籲民眾自備環保購物袋。

公害污染
污染治理
國際新聞
日本
塑膠袋
紙袋
自備購物袋
廢棄物

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

網頁設計最專業,超強功能平台可客製化

美民主黨將提宏大計畫 2035年汽車百分百用乾淨能源

摘錄自2020年6月29日聯合報報導

美國民主黨眾議員預定29日提出一套志向遠大的計畫,尋求在2035年前讓汽車百分百使用乾淨能源,目標是對抗氣候變遷。此舉雖能鞏固既有票倉,但勢必惹怒右派選民。

消息人士透露,這套多達500頁的計畫尋求在2050年達成淨零碳排,和在2035年達成汽車百分之百使用乾淨能源的目標。這項計畫也要求擴大與太陽能相關的稅額抵免措施,這項措施已成功提高太陽能的使用。

這項計畫也要求電力產業在2040年達成淨零碳排。除了其他的環境法規,該計畫也提議徵收碳稅,但對於如何達成這件事隻字未提,多數的立法細節將留給其他委員會。民主黨若在11月的總統大選贏得參眾兩院的多數席次,這項計畫也許會成為施政目標之一。

再生能源
能源議題
能源轉型
國際新聞
美國
乾淨能源
氣候變遷
零碳排放

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

法國市政選舉投票率史上最低 生態綠黨全面崛起

摘錄自2020年6月29日中央社巴黎報導

法國今(28日)舉行市政選舉第二輪投票,僅4成投票率創史上新低。現任巴黎市長伊達戈拿下近半票數獲得連任;總理菲力普也確認回鍋勒哈佛市市長。生態綠黨拿下多個關鍵大城,法媒稱「綠色浪潮」。

生態綠黨在里昂(Lyon)、波爾多(Bordeaux)、史特拉斯堡(Strasbourg)、圖爾(Tours)、安錫(Annecy)、貝桑松(Besancon)等城市都拿下勝選。執政黨共和前進內部人士向費加洛報(Le Figaro)直言:「這已經不只是綠色浪潮,而是綠色海嘯了…2022年大選,會是綠黨崛起。」

法國中間派民眾運動聯盟(UDI)主席拉加德(Jean-Christophe Lagarde)向CNEWS電視頻道指出,「環保政黨在幾個大城取得關鍵勝利 … 這個結果會在馬克宏的左翼製造困難,因為環保政黨和社會黨都將輪替上位」。

公害污染
污染治理
國際新聞
法國
歐洲生態綠黨
運動抗爭

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

月銷量突破3W!顏值高低油耗買了這款車的車主怎麼說

0T,發現2。0T動力非常強勁以及換擋邏輯平順性相比1。5T車型做的要更好。內飾豪華感氛圍很容易打動人,從內飾的用料、裝配、做工都彰顯豪華感,後排地台做到全平,為後排中間乘客提高舒適性。唯一不滿足就是副駕駛沒配備電動調節,略失檔次。

前言

昂科威在中型SUV市場已爭得其應有的一席之地,時尚外觀以及較高配置都要同比同級別對手更為豐富!11月銷量昂科威脫穎而出,11月銷量就已突破3萬輛大關!這對其自身實力也不用懷疑。今天我們就看看已經提車的朋友們,對它的評價怎麼樣。

別克-昂科威

指導價:20.99-34.99萬

車主一:Dawson_道森

購買車型:昂科威 2017款 20T 兩驅豪華版

裸車價格:22.59萬

車主點評:當時第一眼看昂科威科技性能以及酷炫霸氣的外觀設計,就已被它深深吸引,相比其它同級別車型,昂科威兩驅更有駕駛樂趣,油耗有點偏高,但能接受。昂科威這款車性價比高與它動力性能非常突出,即使小玩越野昂科威性能優勢還是非常不錯。昂科威空間以及乘坐感受,都能給滿分,前後排空間非常有優勢,沒有選擇錯!

目前行駛里程:昂科威目前行駛4850公里,剛過首保綜合油耗在7-8L/100km,還是相當滿意。

車主二:半夏

購買車型:昂科威 2017款 28T 四驅精英型

裸車價格:25.39萬

車主點評:原本要選擇1.5T車型,通過試駕2.0T,發現2.0T動力非常強勁以及換擋邏輯平順性相比1.5T車型做的要更好!內飾豪華感氛圍很容易打動人,從內飾的用料、裝配、做工都彰顯豪華感,後排地台做到全平,為後排中間乘客提高舒適性。唯一不滿足就是副駕駛沒配備電動調節,略失檔次。

目前行駛里程:目前行駛2800公里,市區油耗10.3L/100km,高速油耗則能做到6.6L/100km,相當省油!

車主三:眼鏡男阿銳

購買車型:昂科威 2017款 28T 四驅豪華型

裸車價格:27.99萬

車主點評:主要看中昂科威配置豐富,例如氙氣大燈、無鑰匙進入/啟動系統、定速巡航、全景天等實用配置都配備了。操控方面,方向盤轉向輕盈,力度適中以及真皮方向盤摸起來質感非常舒服,靜音方面效比同級別日系車做的更好,即使高速行駛120km,依然沒什麼風噪聲。唯一就是受不了內飾套娃設計風格,沒有眼前一亮的感覺。

目前行駛里程:目前行駛6500公里,綜合油耗為10L/100km,油耗可以接受

全文總結

昂科威是一款綜合性不錯的車型,駕駛起來非常輕鬆,有一定的運動基因,配置方面較為豐富,性價比高,對一台中型SUV來說能擁有優秀燃油經濟性,真的是讓人相當滿意。這也證明其銷量為什麼能有這麼大的成功!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

阿爾卑斯山純淨水源受污染 知名礦泉水Evian驗出微量禁用農藥

摘錄自2020年7月14日自由時報綜合報導

法國知名礦泉水「依雲」(Evian),近日被瑞士專家檢驗發現,礦泉水中含有微量禁用農藥,儘管對人體健康無害,但專家卻擔心,阿爾卑斯山的水源恐受到污染。

瑞士聯邦水科學技術研究所(Swiss Federal Institute of Aquatic Science and Technology)最新檢驗發現,法國依雲礦泉水中,出現6奈克的農藥百菌清(Chlorothalonil),含量仍在規範標準內,仍引起關注。

依雲礦泉水的水源主要來自日內瓦湖畔(Lake Geneva)的依雲鎮(Évian-les-Bains),被認為是阿爾卑斯山的心臟地帶,標榜水質純淨,科學家更用於校準測量儀器。

瑞士自1970年代批准百菌清的使用,根據去(2019)年新的研究結果,瑞士政府將百菌清重新定為「可能致癌物質」,並從今(2020)年初禁止使用。

生物多樣性
土地利用
土地水文
國際新聞
瑞士
飲用水
阿爾卑斯山
水文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

巴國為永續目標 廣植樹、投資綠能

摘錄自2020年7月14日台灣醒報報導

根據《卡達半島新聞》報導,巴基斯坦日前宣佈,已提前達成聯合國推動的2030年永續發展目標。聯合國永續發展部門發出聲明表示,「在聯合國的協助下,巴基斯坦已提前10年達標,我們將共同慶祝此項成就。」

17個永續發展目標是聯合國為呼籲各國採取保護地球行動所設下的願景,包括提升人類健康標準、教育品質、改善水資源、環境衛生條件及減少不平等現象。

巴基斯坦發起的「10億樹林海嘯計劃」經聯合國檢視,確認其符合各項標準。聯合國表示,巴基斯坦推動大規模植樹造林、保護生物多樣性、投資綠色能源、研發電動汽車及推動綠色環境工作廣受各國讚賞,也都能有效對抗氣候變遷。

巴國政府表示:「我們取消了上屆政府簽署的煤炭進口計劃,並以低碳能源及水力發電等項目取代。」全球專家高度讚賞在後疫情時期巴基斯坦推動的綠色刺激計劃,國際自然保護聯盟委員會主席麥金儂博士說:「這將是經濟發展與綠色新政連結的絕佳範本。」

生物多樣性
能源轉型
能源議題
國際新聞
綠色經濟
氣候變遷
棲地保育

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!