2017-01-11 3 views
-1

golangの地図からの読み書きのロックバージョンを実装しようとしましたが、目的の結果が返されません。ロック付き地図からの読み込みはチャンネル経由で値を返しません

パッケージメイン

import (
    "sync" 
    "fmt" 
) 

var m = map[int]string{} 
var lock = sync.RWMutex{} 

func StoreUrl(id int, url string) { 
     for { 
       lock.Lock() 
       defer lock.Unlock() 

       m[id] = url 
     } 
} 

func LoadUrl(id int, ch chan string) { 
    for { 
     lock.RLock() 
     defer lock.RUnlock() 

     r := m[id] 
     ch <- r 
    } 
} 

func main() { 
    go StoreUrl(125, "www.google.com") 

    chb := make(chan string) 
    go LoadUrl(125, chb); 

    C := <-chb 
    fmt.Println("Result:", C)       
} 

出力は次のようになります。値を意味

Result: 

は、私は取得しないチャネルを介して返されません。ロック/ゴルーチンなしでは正常に動作するようです。私は何を間違えたのですか?

コードもここで見つけることができます:スリープ状態またはIOのいくつかの種類なし

https://play.golang.org/p/-WmRcMty5B

+5

は、なぜあなたは1つのURLを格納するための無限ループが必要なのでしょうか? LoadUrlについても同じですか?あなたのローカルマシンでこのコードを試してみましたか? –

+0

はい私は私のローカルマシンでこのコードを試しました、私はそれほど大きな問題を抱えていました。http://stackoverflow.com/a/38140573/132728 – Max

+0

@Max無限ループはどちらも不要です。彼らは[goroutine leakage](https://dave.cheney.net/2016/12/22/never-start-a-goroutine-without-knowing-how-it-will-stop)を引き起こします。 –

答えて

1

無限ループは常に悪い考えです。

StoreUrlの開始位置にprintステートメントを置くと、決して印刷されないことがわかります。つまり、goルーチンは開始されませんでした。go呼び出しは、この新しいgoルーチンに関する情報を設定しています。 goスケジューラーの実行キューが、スケジューラーはまだそのタスクをスケジュールするために実行されていません。あなたはどのようにスケジューラを動かすのですか?スリープ/ IO /チャンネルの読み書きを行います。

もう1つの問題は、無限ループがロックを取っていて、再度ロックを取得しようとしているために、デッドロックが発生することです。 Deferは関数の終了後にのみ実行され、その関数は無限ループのために終了しません。

以下は、スリープを使用してすべての実行スレッドがその作業を行う時間を確保するように修正されたコードです。

package main 

import (
    "sync" 
    "fmt" 
    "time" 
) 

var m = map[int]string{} 
var lock = sync.RWMutex{} 

func StoreUrl(id int, url string) { 
     for { 
       lock.Lock() 
       m[id] = url 
       lock.Unlock() 
       time.Sleep(1) 
     } 
} 

func LoadUrl(id int, ch chan string) { 
    for { 
      lock.RLock() 
      r := m[id] 
      lock.RUnlock() 
      ch <- r 

    } 
} 

func main() { 
    go StoreUrl(125, "www.google.com") 
    time.Sleep(1) 
    chb := make(chan string) 
    go LoadUrl(125, chb); 

    C := <-chb 
    fmt.Println("Result:", C) 
} 

編集: @Jaunコメントで述べたように、あなたはまた、睡眠の代わりにruntime.Gosched()を使用することができます。

+0

runtime.Gosched()のsleep命令の置き換えはどうですか?それはプロセッサーを産み、https://godoc.org/runtime#Gosched –

+0

@JuanCarlosGarcia Yupでも動作するよりも意味があります。 – Ankur

+0

このコードと私のコードw/rとの無限ループの違いは何ですか?何人かの男がコメントしたように、彼らはまた "ゴルーチン漏れ"を引き起こしませんか? – Max

1

deferの使用法は不正ですが、deferは関数の最後で実行され、statementでは実行されません。

func StoreUrl(id int, url string) { 
    for { 
     func() { 
      lock.Lock() 
      defer lock.Unlock() 
      m[id] = url 
     }() 
    } 
} 

または

func StoreUrl(id int, url string) { 
    for { 
     lock.Lock() 
     m[id] = url 
     lock.Unlock() 
    } 
} 

私達は行くルーチンの順序を制御するので、順序を制御するために()time.Sleepを追加することはできません。ここ

コード:

https://play.golang.org/p/Bu8Lo46SA2

+0

私は理由はわかりませんが、あなたのソリューションは、サーバーアプリケーションで使用されたときにアプリケーションのハングアップを引き起こしました。 – Max

+0

すべてのfor文(https://play.golang.org/p/-zzDfFIq-S)にtime.Sleep()を追加するか、自分のマシンで実行する –

関連する問題