Golang 由兩個 chan 組成的鎖死

這是有一次在工作中發生的情況,當時 debug 很久。原來不是只有 Mutex 才會發生鎖死。

以下是一個涉及兩個 channel 的死鎖示例:

雙 Channel 死鎖示例

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        v := <-ch1  // 等待從 ch1 接收數據
        ch2 <- v    // 將接收到的數據發送到 ch2
    }()

    ch1 <- <-ch2  // 主 goroutine 嘗試從 ch2 接收並發送到 ch1
}

這個程序會產生死鎖,原因如下:

  1. 主 goroutine 創建了兩個無緩衝的 channel: ch1ch2
  2. 一個新的 goroutine 被創建,它首先嘗試從 ch1 接收數據,然後將接收到的數據發送到 ch2
  3. 主 goroutine 嘗試從 ch2 接收數據,然後將接收到的數據發送到 ch1
  4. 兩個 goroutine 都被阻塞,等待對方的操作完成,但由於互相依賴,它們永遠無法完成操作。

死鎖的原因

這種情況下發生死鎖的根本原因是:

  1. 兩個 goroutine 都在等待對方完成操作。
  2. 使用了無緩衝的 channel,導致每個發送操作都需要有相應的接收操作同時進行。
  3. 操作順序的相互依賴形成了一個循環等待的情況。

如何避免

要避免這種死鎖,可以採取以下方法:

  1. 使用帶緩衝的 channel:
   ch1 := make(chan int, 1)
   ch2 := make(chan int, 1)
  1. 改變操作順序,打破循環依賴:
   go func() {
       ch2 <- 1    // 首先向 ch2 發送數據
       v := <-ch1  // 然後從 ch1 接收數據
   }()

   v := <-ch2    // 主 goroutine 首先從 ch2 接收數據
   ch1 <- v      // 然後向 ch1 發送數據
  1. 使用 select 語句來同時處理多個 channel:
   select {
   case v1 := <-ch1:
       ch2 <- v1
   case v2 := <-ch2:
       ch1 <- v2
   default:
       // 避免阻塞
   }
  1. 使用 context 或 done channel 來實現超時或取消機制:
   ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   defer cancel()

   select {
   case v := <-ch1:
       ch2 <- v
   case <-ctx.Done():
       fmt.Println("Operation timed out")
   }

通過這些方法,我們可以有效地避免涉及多個 channel 的死鎖情況,提高程序的健壯性和可靠性。

Read more

n8n怎麼做防抖debounce?

n8n怎麼做防抖debounce?

防抖 debounce, 是程式設計重要的概念之一。 用意是短時間有多個訊息進來,只處理一次。 處理哪一次呢?只處理最後一次。 就比如,有人點擊習慣什麼都按兩下, 你就要每次都處理兩次嗎? 又比如,你做 Line 機器人。講一句回一句。 那如果使用者一次傳好幾句呢? 像是打錯字,習慣的修正,再送一次。或是使用者分段講完。 你要跑一次一起處理,還是跑多次? 現在你知道使用情境了,那在 n8n 怎麼做呢? 就比如 Line 訊息好了,你其實可以把Webhook 來的訊息存到 db 裡。 另外做一個 短時間(ex:10s) 就跑一次的 schedule, 去檢查新訊息,並休息一下(ex:5s)。再檢查新訊息。 如果兩次新訊息,筆數都一樣,就表示沒有新訊息進來了。就可以開始處理。 最後再把新訊息標記成舊訊息。 讓

By Mason Tang
n8n 主從架構,解放n8n效能,進行更多任務

n8n 主從架構,解放n8n效能,進行更多任務

n8n 有提供主從架構,讓多個 n8n 程式一起為你工作。 他們會懂得調派任務。 比如你把任務給主管,主管收到任務,就會把任務分配給底下的員工。而你身為老闆的你,只要面對主管。 這主管就是 Master, 員工就是 Slave (奴隸,真貼切) 透過多個程式,讓效率大幅提高。同時間能進行的任務更多。 那實際怎麼做呢? 設定 N8N_ENCRYPTION_KEY 這是用來加密資料庫資料的密鑰,在原本單一 n8n ,不是那麼需要,啟動時就會幫你建立。 會存在 .n8n/config 中,長得就像這樣 { "encryptionKey": "cjw5GKuWL6eoqaC0MOnHdBNWOfxAzXsn" } 今天你要跑多個 n8n ,每個 n8n 都要讀資料庫,那些加密的資料就需要同樣的 encryptionKey 才能讀取。 所以需要直接在環境變數中直接設定

By Mason Tang
n8n 做個計數器,保存變數到下一次執行

n8n 做個計數器,保存變數到下一次執行

n8n 可能有人會好奇,怎麼做計數器,例如一天只能使用 200次。 但是 Node 裡,似乎沒看到這功能。 但其實這功能就藏在 Code 裡 獲取靜態資料 其實這功能就藏在 $getWorkflowStaticData 使用這個函數,拿到的物件,其實是能持久化的,即使 n8n 關掉再開,資料也還在。 整個 n8n 共用這變數 使用 $getWorkflowStaticData('global') 這邊可以看到,這個 count 已經被使用 9 次了,並且在其他 Code 也可以獲得這個 count 單一 Node 使用,不能跨 Node 使用 使用 $getWorkflowStaticData('node&

By Mason Tang
n8n 教學,匯出/匯入所有憑證,輕鬆搞定 n8n 搬家

n8n 教學,匯出/匯入所有憑證,輕鬆搞定 n8n 搬家

上次介紹了如何在 Zeabur, Docker, Node.js 部署自動化工具 n8n。 n8n教學-搭建自動化工具 n8n 的三個方案近幾年隨著 AI 發展,AI Agent 和自動化也成為企業轉型和提升效率的重要工具。這些技術不僅能夠處理大量的數據分析,還能自動完成重複性高、耗時的任務,讓員工能夠專注於更具創造性和戰略性的工作。 今天就教你,搭建自動化工具 n8n 的三個方案。 三種搭建n8n的方案 1. 使用 Zeabur 的 Saas 服務 2. 使用 Docker 部署在自家機器上 3. 使用 Node.js 直接啟動在自家機器上 使用 Zeabur 的 Saas 服務 這我也是看雷蒙才知道有 Zeabur 這平台,的確是新手很適合的入門。也推薦給大家使用 優點:

By Mason Tang