私は最近、Goで同時にRead
とClose
を正しく理解する方法がわかりません。私の特定のケースでは、私はシリアルポートでそれを行う必要がありますが、問題はより一般的です。並行読み込み/終了、Goで、クロスプラットフォームの方法で
私たちは、物事を同期させるために特別な努力をしなければ、競合状態に陥ります。簡単な例:
closing
==================
WARNING: DATA RACE
Write at 0x00c4200143c0 by main goroutine:
os.(*file).close()
/usr/local/go/src/os/file_unix.go:143 +0x124
os.(*File).Close()
/usr/local/go/src/os/file_unix.go:132 +0x55
main.main()
/home/dimon/mydata/projects/go/src/dmitryfrank.com/testfiles/main.go:20 +0x13f
Previous read at 0x00c4200143c0 by goroutine 6:
os.(*File).read()
/usr/local/go/src/os/file_unix.go:228 +0x50
os.(*File).Read()
/usr/local/go/src/os/file.go:101 +0x6f
main.reader()
/home/dimon/mydata/projects/go/src/dmitryfrank.com/testfiles/main.go:27 +0x8b
Goroutine 6 (running) created at:
main.main()
/home/dimon/mydata/projects/go/src/dmitryfrank.com/testfiles/main.go:16 +0x81
==================
Found 1 data race(s)
exit status 66
[OK]を、どのように適切にそれを処理するために:私たちはmain.go
として上記を保存し、go run --race main.go
を実行
package main
import (
"fmt"
"os"
"time"
)
func main() {
f, err := os.Open("/dev/ttyUSB0")
if err != nil {
panic(err)
}
// Start a goroutine which keeps reading from a serial port
go reader(f)
time.Sleep(1000 * time.Millisecond)
fmt.Println("closing")
f.Close()
time.Sleep(1000 * time.Millisecond)
}
func reader(f *os.File) {
b := make([]byte, 100)
for {
f.Read(b)
}
}
場合は、次のように出力が見えるだろうか?もちろん、f.Read()
を呼び出す前にいくつかのミューテックスをロックすることはできません。なぜなら、ミューテックスは基本的にすべての時間ロックされてしまうからです。正常に動作させるためには、条件変数のように読み込みとロックの間に何らかの協力が必要です。ゴルーチンを待たせる前にmutexがロックされ、ゴルーチンが起きたときにロックバックされます。
私は手動でこのようなものを実装しますが、次に読んでいる間に何か方法が必要です。select
このように:(擬似コード)
select {
case b := <-f.NextByte():
// process the byte somehow
default:
}
私はパッケージosとsyncのドキュメントを検討し、これまでのところ、私はそれを行うにはどのような方法が表示されません。メイン、その読者に伝えるための
を終了しました> -
- メイン:私は信じて
実際にファイルを閉じる必要はありますか?最も安全な方法は、プロセスが終了するまで読書ゴルーチンを放置することです。 – JimB
私は、異なるスレッド実行時にファイルハンドルを使用する理由、またはすべてのリソースに関して、なぜ使用する必要があるのかわかりません。それはあなたのコードを複雑にするだけです。 – Ankur
@JimB、私は再接続を実装するためにファイルを閉じる必要があります。ノードが '/ dev/ttyUSB0'だったデバイスのプラグを抜いてファイルを閉じないと、'/dev/ttyUSB0'ファイルが開かれ、デバイスを接続すると、 '/ dev/ttyUSB1'です。もう一度 '/ dev/ttyUSB0'にする必要があります。 –