関数または関数の本体をgolangの2つのスレッドから呼び出さないようにするにはどうすればよいですか?golangで関数スレッドを安全にする方法
私は、一度に1つの発信者しか持つことのできないシリアルインターフェイスを呼び出しているWebサーバーを使用しています.2つの呼び出しは、シリアル回線上で相互にノイズを発生させることによって互いに打ち消し合います。
関数または関数の本体をgolangの2つのスレッドから呼び出さないようにするにはどうすればよいですか?golangで関数スレッドを安全にする方法
私は、一度に1つの発信者しか持つことのできないシリアルインターフェイスを呼び出しているWebサーバーを使用しています.2つの呼び出しは、シリアル回線上で相互にノイズを発生させることによって互いに打ち消し合います。
最も簡単な方法は、sync.Mutex
を使用することです:
package main
import (
"fmt"
"sync"
"time"
)
var lock sync.Mutex
func main() {
go importantFunction("first")
go importantFunction("second")
time.Sleep(3 * time.Second)
}
func importantFunction(name string) {
lock.Lock()
defer lock.Unlock()
fmt.Println(name)
time.Sleep(1 * time.Second)
}
あなたは「第一」および「第二は、」彼らが行くのルーチンであっても、1秒間隔で印刷されていることがわかります。
囲碁遊び場:彼が言うようにミューテックスを使用してhttps://play.golang.org/p/mXKl42zRW8
Pylinuxのソリューションは、あなたのケースでは、おそらく最も簡単です。私はここで別のものを追加します。あなたの場合は適用される場合とされない場合があります。
ミューテックスを使用する代わりに、単一のゴルーチンがシリアルインターフェイス上のすべての操作を実行し、チャネルを使用して実行する必要がある作業をシリアル化することができます。 Example:
package main
import (
"fmt"
"sync"
)
// handleCommands will handle commands in a serialized fashion
func handleCommands(opChan <-chan string) {
for op := range opChan {
fmt.Printf("command: %s\n", op)
}
}
// produceCommands will generate multiple commands concurrently
func produceCommands(opChan chan<- string) {
var wg sync.WaitGroup
wg.Add(2)
go func() { opChan <- "cmd1"; wg.Done() }()
go func() { opChan <- "cmd2"; wg.Done() }()
wg.Wait()
close(opChan)
}
func main() {
var opChan = make(chan string)
go produceCommands(opChan)
handleCommands(opChan)
}
ミューテックスに、この相対的な利点は、あなたが待機キューをより詳細に制御を持っているということです。ミューテックスでは、キューは暗黙的にLock()
に存在し、無制限です。一方、チャネルを使用すると、待機中の発信者の最大数を制限し、同期したコールサイトに過負荷がかかった場合に適切に反応することができます。 len(opChan)
でキューに入っているゴルーチンの数を確認するなどの作業を行うこともできます。
追加、編集:
制限を上記の例では、(コメント欄で述べたように)、それは元の送信者に演算からの戻り結果を処理しないことです。チャネルを使用するアプローチを維持しながら、これを行う1つの方法は、各コマンドに結果チャネルを導入することです。だからではなく、コマンドチャネルを介して文字列を送信するのでは、1次の形式の構造体を送信することができ、次のよう
type operation struct {
command string
result chan string
}
コマンドは、コマンドチャネルにエンキューされます:
func enqueueCommand(opChan chan<- operation, cmd string) <-chan string {
var result = make(chan string)
opChan <- operation{command: cmd, result: result}
return result
}
これはにコマンドハンドラを可能にコマンドの発信者に値を送り返します。遊び場の完全な例here。
機能の結果が私にとって重要である(シリアル回線からの返信)ので、私の問題を解決できないとあなたは正しいと思います。私がチャンネルを使用した場合、双方向チャンネルが応答していた。しかし、これは慣れ親しんだように感じるので、追加していただければ幸いです:-) – Pylinux
完全性のために、あなたの 'main()'は最後に 'close(opChan)'を持つべきです。それから、睡眠を取り除き、第2のゴルーチンを新しいゴルーチン(すなわち、メインの部分の代わりに)にするのは良いでしょう。その後、終了はきれいに起こります。 –
Pylinuxの場合、この答えが*もっと*イディオム的な解決策であることを示唆する良い例があります。これは、Goの推奨事項「メモリを共有して通信しませんが、通信することでメモリを共有します」に従います。この場合、共有エンティティはシリアルデバイス内にあります。どちらのアプローチももちろん動作します。ミューテックスがより高速になる可能性があります。しかし、CSPのチャネルとゴルーチンは、より簡単に大きな集約を構成できるため、チャネルアプローチはどんな大規模ネットワークにも適しています。 –
非リエントラント機能を実現するには、2つの方法があります。
実行されている間に最初の呼び出し元は、関数、後続の呼び出し元(S)アボートを実行します
非リエントラント機能のブロックは、@ Pylinuxの答えに記載されているように、mutex
で最も簡単に実装できます。上記で
import (
"sync/atomic"
"time"
)
func main() {
tick := time.Tick(time.Second)
var reentranceFlag int64
go func() {
for range tick {
go CheckSomeStatus()
go func() {
if atomic.CompareAndSwapInt64(&reentranceFlag, 0, 1) {
defer atomic.StoreInt64(&reentranceFlag, 0)
} else {
return
}
CheckAnotherStatus()
}()
}
}()
}
CheckAnotherStatus()
が再入最初の発信者が設定するようreentranceFlag
1
にから保護され、そして後続の発信者は失敗次のよう 降伏非リエントラント関数は、&スワップを比較原子を介して実現することができます同じことをする、そして終了する。
私のブログ記事Implementing non re-entrant functions in Golangをより丁寧な議論に考慮してください。
また、 'importantFunction'のロジック内に何かパニックが生じた場合に備えて、' lock.Lock() 'の直後に' defer lock.Unlock() 'を置くことが望ましいかもしれません。そうすれば、 'Unlock'呼び出しが確実に行われます。 –
@JosefGrahn良い点私の例を更新しよう! – Pylinux