

で、型番を調べたら、



江端智一のホームページ


で、型番を調べたら、



(1)redisサーバを援用して、(2)golangをサーバにして、(3)websocketでブロードキャストが実現できて、(4)JavaScriptをクライアントとして使える、(5)OSSを捜し出しました。
理由は、複数のWebに同じ地図と車両の移動をリアルタイムを表示しなればならないのですが、Websocketはユニキャスト通信しかできないので、困っていました(UDPとか使えればいいんですが、スマホでUDPが通るとは思えないので)。
WebSocketでブロードキャストを作るのが辛いので、これを援用したいと思います。

これは、http://localhost:5000 でブラウザを立ち上げると、サーバを介してブラウザ→ブラウザにメッセージがechoされます。
redisサーバを立ち上げておいて、main.goと、js/index.htmlだけで動きます。
上記を改造すれば、サーバからパブ(pub)、webブラウザでサブ(sub)が可能になるはずです ―― 多分
実験した結果、subscribeしたオブジェクトに、過去のデータもpublishされることが分かりました。
subscribe以前のデータを取込むと逆に困る(過去のデータは不要な上に、データの表示もバラバラになる)ので、pubsub-goの採用は見送り、redigoの方を使うことにしました。
JavaScriptを直接のクライアントとして使いたかったのですが、redigoと連携するJavaScriptが見つけられなかった(かなり捜したつもり)ので、Golangで立ち上げるハンドルの中で、個別に対処することにしました。
golangのプログラムの中でブロードキャストするのであれば、
https://github.com/MicrosoftArchive/redis/releases/tag/win-3.2.100
で、redisサーバをインスールして、
https://github.com/gomodule/redigoを使うという手もありますが、
のサンプルプログラムで簡単に試すことができます。
PrumeClusterは、Leafletをベースとして動く、スケーラブルオブジェクトビューアです。1万オブジェクトくらいなら軽く表示できます。
しかし、PrumeClusterは、クライアントのブラウザの中にアイコンのオブジェクトを直接作るので、基本的にはサーバとして使うことができません。クライアントとして使うものであて、描画画面は、常に"1つ"です。
で、私が作ったPrumeMobileもベースは、PrumeClusterなので、サーバとして使うことはできないのですが ―― 今週末、Vue.jpとかでスマホクライアント作ろうかと思ったのですが ――『もう新しいこと覚えるのは嫌だ』と思い知り、PrumeMobileのサーバ化を試みています ――ひとえに、考えうる限り、手を抜きたい、という一心からです。

複数のJavaScriptに対して、PrumeClusterがメッセージをブロードキャスト送付してくれるのか、くれないのか、明日調べよう。
最近"https"縛りがきつくて、ローカルのindex.htmlを叩くだけでは、画面が出てこなくなりました。正直、面倒くさいなぁ、と思っています。
こちらは、表示画面でブラウザを使いたいだけなのに、ブラウザ(特にchrome)が煩いことこの上もない。
これも時代の流れか、と諦めて、index.htmlを書いているディレクトリの内容で、サクッとサーバを立てる方法を、色々やってみましたので、メモを残しておきます。
まず、node.jsをインストールしてnpmを使えるようにしておきます。
面倒なので、私の環境に合わせて説明しますね(このディレクトリを隠す人、多いですけど、はっきり言って読み難い上に、あまり意味ない(外部から、ディレクトリに入れるところまでハックされたら、何をしても無駄))
という訳で、私の作業ディレクトリは、ここ→ ~/kese/leaflet です。
$ npm install -g http-server
$ http-server
と、これだけで、
ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/leaflet$ http-serverStarting up http-server, serving ./http-server version: 14.1.0http-server settings:CORS: disabledCache: 3600 secondsConnection Timeout: 120 secondsDirectory Listings: visibleAutoIndex: visibleServe GZIP Files: falseServe Brotli Files: falseDefault File Extension: noneAvailable on:http://192.168.0.8:8080http://127.0.0.1:8080http://172.28.64.1:8080http://172.21.112.1:8080Hit CTRL-C to stop the server

でもって、ここから、Windows10で使えそうなバイナリをダウンロードしました。

ダウンロードしたところから、直接叩いてみたら、C:\Users\Ebata\AppData\Local\mkcert の中に、鍵ができていましたが、最初に、mkcer -installしろ、と言われています。

で、"localhost+1-key.pem"を "key.pem"とリネームして、"localhost+1.pem"を"cert.pem"とリネームして、~/kese/leafletに放り込みます。
そんでもって、~/kese/leaflet から
$ http-server -S -C cert.pem -o -p 8081
とすると、https 対応のサーバが立ち上がります。
ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/leaflet$ http-server -S -C cert.pem -o -p 8081Starting up http-server, serving ./ through httpshttp-server version: 14.1.0http-server settings:CORS: disabledCache: 3600 secondsConnection Timeout: 120 secondsDirectory Listings: visibleAutoIndex: visibleServe GZIP Files: falseServe Brotli Files: falseDefault File Extension: noneAvailable on:https://192.168.0.8:8081https://127.0.0.1:8081https://172.28.64.1:8081https://172.21.112.1:8081Hit CTRL-C to stop the serverOpen: https://127.0.0.1:8081

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Open Street Map Test</title>
<style type="text/css">
html,body{ margin: 0px; }
</style>
<!--
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
-->
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
<script type="text/javascript" src="https://openlayers.org/api/2.13.1/OpenLayers.js"></script>
<script type="text/javascript">
// グーローバル変数の定義
var od;
var des_lonlat;
var arr_lonlat;
</script>
<script>
function MapInit(){
map = new OpenLayers.Map("MapCanvas");
var mapnik = new OpenLayers.Layer.OSM();
map.addLayer(mapnik);
//var lonLat = new OpenLayers.LonLat(139.47552, 35.59857)
var lonLat = new OpenLayers.LonLat(139.796182, 35.654285)
.transform(
new OpenLayers.Projection("EPSG:4326"),
new OpenLayers.Projection("EPSG:900913")
);
map.setCenter(lonLat, 17);
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
initialize: function(options) {
this.handler = new OpenLayers.Handler.Click(
this, {
'click': this.onClick
}, this.handlerOptions
);
},
onClick: function(e) {
var lonlat = map.getLonLatFromPixel(e.xy);
lonlat.transform(
new OpenLayers.Projection("EPSG:900913"),
new OpenLayers.Projection("EPSG:4326")
);
var markers = new OpenLayers.Layer.Markers("Markers");
map.addLayer(markers);
var marker = new OpenLayers.Marker(
new OpenLayers.LonLat(lonlat.lon, lonlat.lat)
.transform(
new OpenLayers.Projection("EPSG:4326"),
new OpenLayers.Projection("EPSG:900913")
)
);
markers.addMarker(marker);
$("#LonLat").html("lon:" +lonlat.lon+ " lat:" +lonlat.lat);
if (od == "arrival"){
arr_lonlat = lonlat;
alert("arr_lonlatが設定されました" + arr_lonlat.lon +" " + arr_lonlat.lat);
} else if (od == "destination"){
des_lonlat = lonlat;
alert("des_lonlatが設定されました" + des_lonlat.lon +" " + des_lonlat.lat);
}
}
});
var click = new OpenLayers.Control.Click();
map.addControl(click);
click.activate();
}
</script>
<script type="text/javascript">
$(document).ready(function () {
$("#button01").on('click', function () {
od = "destination";
alert(od + " ボタン1がクリックされました。");
});
$("#button02").on('click', function () {
od = "arrival";
alert(od + " ボタン2がクリックされました。");
});
$("#button03").on('click', function () {
od = "confirmed"
alert(od + " ボタン3がクリックされました。");
//
});
})
</script>
</head>
<body>
<div id="MapCanvas" style="width:700px;height:700px;"></div>
<div id="LonLat"></div>
<input id="button01" type="button" value="Button1"/>
<input id="button02" type="button" value="Button2" />
<input id="button03" type="button" value="Button3" />
<script type="text/javascript">MapInit();</script>
</body>
</html>
■外部から強制的に書き込もうとすると失敗します
ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ docker ps
095fe60f02cc nginx:1.15-alpine "nginx -g 'daemon of…" 8 weeks ago Up 22 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp xxxxxxx_ride_hailing_go_nginx_1
でもって、
ubuntu@ip-172-26-7-19:~/codes/xxxxx_ride_hailing_go$ sudo docker cp fullchain.pem 095fe60f02cc://etc/nginx/conf.d/
Error response from daemon: Error processing tar file(exit status 1): unlinkat /etc/nginx/conf.d/fullchain.pem: device or resource busy (失敗します)
しかし、dockerの中に入って、
ubuntu@ip-172-26-7-19:~/codes/xxxxxx_ride_hailing_go$ docker exec -i -t xxxxx_ride_hailing_go_nginx_1 sh
で内側から、killすると、dockerごとダウンして、なんとも悩ましい、にわとりと卵の関係になってしまいます。
■解決方法
ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ sudo docker cp fullchain.pem 095fe60f02cc://etc/nginx/conf.d/fullchain.pem.new
ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ sudo docker cp privkey.pem 095fe60f02cc://etc/nginx/conf.d/privkey.pem.new
でもって、 (今後は、new2, new3, new4 とか、どんどん増やしていく)
ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ vi nginx.conf
をして、
———————————————————
ssl_certificate /etc/nginx/conf.d/fullchain.pem.new;
ssl_certificate_key /etc/nginx/conf.d/privkey.pem.new;
———————————————————
と書き換える。
さらに、
docker-compose.yml の最後の行に、
volumes:
- "./nginx.conf:/etc/nginx/conf.d/default.conf"
- "./fullchain.pem:/etc/nginx/conf.d/fullchain.pem.new"
- "./privkey.pem:/etc/nginx/conf.d/privkey.pem.new"
も変える。
でもって、 docker-compose stop; docker-compose start をすれば、新しい鍵で動き出します。不細工ではありますが、プログラムは動くようになります。
■ところで今思いついたんだけど、いい方法がありました。
現在は、fullchain.pem.new と、privkey.pem.new で動いているのだから、fullchain.pem と、privkey.pemは、rm できるはず。
で、cp fullchain.pem.new fullchain.pem, cp privkey.pem.new privkey.pem として、さらに、nginx.confの設定を元に戻せば、fullchain.pem と、privkey.pem の名前を使えるはずです
(まあ、動いているので、今は、このままにしています)
■もっと、いい方法がありました。
docker-compose.yml の最後の行に、
volumes:
- "./nginx.conf:/etc/nginx/conf.d/default.conf"
- "./fullchain.pem:/etc/nginx/conf.d/fullchain.pem"
- "./privkey.pem:/etc/nginx/conf.d/privkey.pem"
の3行を加えれば、Dockerの外側に、最新の"fullchain.pem"と、"privkey.pem"を置いておけば、自動的にDockerコンテナの中に取り込まれます(今、そうなっている)
最近の文章は、ほとんど、Visual Studio Code(vscode) でMarkdownを使って書いています。超ラクです。
で、「図面のコピペをMarkdownの文書の中にサクっと入れる」ことができないかな、と、ちょっとググってみたのですが、3秒で見つかりました。
https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image
メモとして、以下に記載しておきます。
■クリップボードから直接マークダウン/asciidoc(または他のファイル)に画像を貼り付けます。
準備: VSCODEの「拡張機能(Ctl+Shift+X)」から、"Paste Image"で検索→インストールを実施
使い方:
(1)画面をクリップボードに取り込む
(2)コマンドパレットを開く。Ctrl+Shift+P (MacではCmd+Shift+P)と入力します。
(3)"Paste Image"と入力するか、Ctrl+Alt+V (MacではCmd+Alt+V)と入力します。
(4)画像は、現在の編集ファイルを含むフォルダに保存されます。相対パスは、現在の編集ファイルに貼り付けられます。
以上
を教えて貰って、喜んでいたのですが、私のようなカルトな使い方をしているケースでは、正しくデータ送信ができない場合があるようです。
動的に登場して、自動的に消滅するような複数の(かなりの数の)goroutineに対しては、受信データの値が変な値になるようです。
下記は、データ送信元のオブジェクトが、自分の位置情報を撒き散らしながらbroadcast送信をしています。

下記は、この途中からgoroutineをバラバラに30個くらい作って受信したものの一つです。

なんども調べてみたのですが、やはりバグが原因ではないようです。
本日、バグを発見しました! 構造体に送信元のオブジェクトを指定する変数が入っていなかった為、全部同じブロードキャストとして受信先が受信をしてしまっていました(03/18)。
ここから得られる結論は、Sync.Cond、broadcastを使うには、
・broadcast、sync.cond は、最初からgoroutineができあがっている場合
・少量、低速にデータ配信を行う場合
でないと、安定的に動作させるには厳しいようです。
これは、まあ、間違ってはいませんです。
==== 追記 =====
そういえば、ブロードキャストの送信者が1であった場合は問題がなかったけど、今回は、送信者が3になったところから、変な動きをしだしたことから鑑みて、
・送信者、受信者は1:Nの関係でないとだめ
なのかもしれないです。
これは確定です。詳しくは以下をご覧下さい↓
以上
の、最後に記載した、
そういえば、ブロードキャストの送信者が1であった場合は問題がなかったけど、今回は、送信者が3になったところから、変な動きをしだしたことから鑑みて、
・送信者、受信者は1:Nの関係でないとだめ
なのかもしれないです。
として、別方式を考えているのですが、「Goの複数のgoroutineに対する、一斉ブロードキャスト」の便利さが、どうにも諦めきれなくて、まだ調べています。
そこで、簡易プログラムで以下の検証を行いました。
目的は以下の通り。
・送信者(sender)(ただし1人)や、受信者(receiver)がランダムなタイミングで出現・消滅しても、ちゃんと動くか
を検証してみました。
// go run main3.go
/*
boardcast sync.bc 実験
(1) 送信側(sender)をgoroutineにて大丈夫か
(2) 受信側(sender)のgoroutineを、送信側の前後で、
適当なタイミングで生成して、消滅させても大丈夫か
*/
package main
import (
"fmt"
"log"
"math/rand"
"sync"
"time"
)
type BroadCaster struct {
cond *sync.Cond
id int64
msg string
}
func (bc *BroadCaster) Send(msg string) {
bc.cond.L.Lock()
defer bc.cond.L.Unlock()
bc.id++
bc.msg = msg
bc.cond.Broadcast()
}
func (bc *BroadCaster) Recv(last int64) (int64, string) {
bc.cond.L.Lock()
defer bc.cond.L.Unlock()
for bc.id == last {
bc.cond.Wait()
}
return bc.id, bc.msg
}
var (
broadcaster = &BroadCaster{
cond: sync.NewCond(&sync.Mutex{}),
}
)
func receiver(i int) {
delete_count := 5 + rand.Intn(10) // ループ回数は5~14回のどれか
log.Println("recv:", i, " start")
defer log.Println("recv:", i, " stop")
last := int64(0)
for k := 0; k < delete_count; k++ {
id, msg := broadcaster.Recv(last)
last = id
log.Println("recv:", i, msg)
}
}
func sender() {
for i := 0; i < 20; i++ { // 20回程度送ってみる
time.Sleep(1 * time.Second)
broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
}
}
func main() {
for i := 0; i < 3; i++ {
go receiver(i)
}
/*
for i := 0; i < 100; i++ {
time.Sleep(1 * time.Second)
broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
}
*/
go sender()
for i := 4; i < 6; i++ {
go receiver(i)
time.Sleep(1 * time.Second)
}
time.Sleep(100 * time.Second) // 排他処理を書くのが面倒なので、ここでmainを眠らせておく
}
これで得られた結果です。
c:\Users\ebata\goga\1-7\test>go run main3.go
go run main3.go
go run main3.go
2022/03/18 12:39:02 recv: 0 start
2022/03/18 12:39:02 recv: 2 start
2022/03/18 12:39:02 recv: 4 start
2022/03/18 12:39:02 recv: 1 start
2022/03/18 12:39:03 recv: 1 hello, world: 0
2022/03/18 12:39:03 recv: 4 hello, world: 0
2022/03/18 12:39:03 recv: 2 hello, world: 0
2022/03/18 12:39:03 recv: 0 hello, world: 0
2022/03/18 12:39:03 recv: 5 start
2022/03/18 12:39:03 recv: 5 hello, world: 0
2022/03/18 12:39:04 recv: 4 hello, world: 1
2022/03/18 12:39:04 recv: 5 hello, world: 1
2022/03/18 12:39:04 recv: 2 hello, world: 1
2022/03/18 12:39:04 recv: 0 hello, world: 1
2022/03/18 12:39:04 recv: 1 hello, world: 1
2022/03/18 12:39:05 recv: 5 hello, world: 2
2022/03/18 12:39:05 recv: 1 hello, world: 2
2022/03/18 12:39:05 recv: 4 hello, world: 2
2022/03/18 12:39:05 recv: 2 hello, world: 2
2022/03/18 12:39:05 recv: 0 hello, world: 2
2022/03/18 12:39:06 recv: 1 hello, world: 3
2022/03/18 12:39:06 recv: 5 hello, world: 3
2022/03/18 12:39:06 recv: 4 hello, world: 3
2022/03/18 12:39:06 recv: 2 hello, world: 3
2022/03/18 12:39:06 recv: 0 hello, world: 3
2022/03/18 12:39:07 recv: 0 hello, world: 4
2022/03/18 12:39:07 recv: 4 hello, world: 4
2022/03/18 12:39:07 recv: 2 hello, world: 4
2022/03/18 12:39:07 recv: 5 hello, world: 4
2022/03/18 12:39:07 recv: 1 hello, world: 4
2022/03/18 12:39:08 recv: 1 hello, world: 5
2022/03/18 12:39:08 recv: 4 hello, world: 5
2022/03/18 12:39:08 recv: 0 hello, world: 5
2022/03/18 12:39:08 recv: 5 hello, world: 5
2022/03/18 12:39:08 recv: 0 stop
2022/03/18 12:39:08 recv: 5 stop
2022/03/18 12:39:08 recv: 2 hello, world: 5
2022/03/18 12:39:09 recv: 2 hello, world: 6
2022/03/18 12:39:09 recv: 1 hello, world: 6
2022/03/18 12:39:09 recv: 4 hello, world: 6
2022/03/18 12:39:10 recv: 4 hello, world: 7
2022/03/18 12:39:10 recv: 2 hello, world: 7
2022/03/18 12:39:10 recv: 1 hello, world: 7
2022/03/18 12:39:11 recv: 1 hello, world: 8
2022/03/18 12:39:11 recv: 4 hello, world: 8
2022/03/18 12:39:11 recv: 2 hello, world: 8
2022/03/18 12:39:12 recv: 2 hello, world: 9
2022/03/18 12:39:12 recv: 4 hello, world: 9
2022/03/18 12:39:12 recv: 1 hello, world: 9
2022/03/18 12:39:13 recv: 1 hello, world: 10
2022/03/18 12:39:13 recv: 2 hello, world: 10
2022/03/18 12:39:13 recv: 4 hello, world: 10
2022/03/18 12:39:14 recv: 4 hello, world: 11
2022/03/18 12:39:14 recv: 1 hello, world: 11
2022/03/18 12:39:14 recv: 1 stop
2022/03/18 12:39:14 recv: 2 hello, world: 11
2022/03/18 12:39:14 recv: 4 stop
2022/03/18 12:39:15 recv: 2 hello, world: 12
2022/03/18 12:39:16 recv: 2 hello, world: 13
2022/03/18 12:39:16 recv: 2 stop
結論としては、
・recvierは、データを重複することなく、またロストすることなく、1つづつキレイに受けとっていた
ということになります。
さて、ここで、今度は、送信者を2人にしてみます。送信者の名前が見易いように、"111"と"999"の番号を付けてます(冗長ですが、プログラムリスト全部を掲載します(私の為に))
// go run main3.go
/*
boardcast sync.bc 実験
(1) 送信側(sender)のgoroutineを2つにしたらどうなるか
*/
package main
import (
"fmt"
"log"
"math/rand"
"sync"
"time"
)
type BroadCaster struct {
cond *sync.Cond
id int64
msg string
}
func (bc *BroadCaster) Send(msg string) {
bc.cond.L.Lock()
defer bc.cond.L.Unlock()
bc.id++
bc.msg = msg
bc.cond.Broadcast()
}
func (bc *BroadCaster) Recv(last int64) (int64, string) {
bc.cond.L.Lock()
defer bc.cond.L.Unlock()
for bc.id == last {
bc.cond.Wait()
}
return bc.id, bc.msg
}
var (
broadcaster = &BroadCaster{
cond: sync.NewCond(&sync.Mutex{}),
}
)
func receiver(i int) {
delete_count := 5 + rand.Intn(10) // ループ回数は5~14回のどれか
log.Println("recv:", i, " start")
defer log.Println("recv:", i, " stop")
last := int64(0)
for k := 0; k < delete_count; k++ {
id, msg := broadcaster.Recv(last)
last = id
log.Println("recv:", i, msg)
}
}
func sender(i int) {
for k := 0; k < 20; k++ { // 20回程度送ってみる
time.Sleep(1 * time.Second)
broadcaster.Send(fmt.Sprintf("hello, world: %d from %d", k, i))
}
}
func main() {
for i := 0; i < 3; i++ {
go receiver(i)
}
/*
for i := 0; i < 100; i++ {
time.Sleep(1 * time.Second)
broadcaster.Send(fmt.Sprintf("hello, world: %d", i))
}
*/
go sender(111) // ここに注意
go sender(999) // ここに注意
for i := 4; i < 6; i++ {
go receiver(i)
time.Sleep(1 * time.Second)
}
time.Sleep(100 * time.Second) // 排他処理を書くのが面倒なので、ここでmainを眠らせておく
}
結果は以下の通りになりました。
2022/03/18 12:56:45 recv: 4 start
2022/03/18 12:56:45 recv: 1 start
2022/03/18 12:56:45 recv: 0 start
2022/03/18 12:56:45 recv: 2 start
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 999
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 5 start
2022/03/18 12:56:46 recv: 5 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 0 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 4 hello, world: 0 from 111
2022/03/18 12:56:46 recv: 1 hello, world: 0 from 111
2022/03/18 12:56:47 recv: 1 hello, world: 1 from 111
2022/03/18 12:56:47 recv: 4 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 1 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 2 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 0 hello, world: 1 from 999
2022/03/18 12:56:47 recv: 5 hello, world: 1 from 111
2022/03/18 12:56:47 recv: 5 hello, world: 1 from 999
2022/03/18 12:56:48 recv: 1 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 5 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 2 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 0 hello, world: 2 from 999
2022/03/18 12:56:48 recv: 4 hello, world: 2 from 999
2022/03/18 12:56:49 recv: 4 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 1 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 5 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 0 hello, world: 3 from 999
2022/03/18 12:56:49 recv: 0 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 1 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 4 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 5 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 5 stop
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 111
2022/03/18 12:56:49 recv: 1 stop
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 111
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 999
2022/03/18 12:56:50 recv: 0 hello, world: 4 from 111
2022/03/18 12:56:50 recv: 0 hello, world: 4 from 999
2022/03/18 12:56:50 recv: 4 hello, world: 4 from 999
2022/03/18 12:56:51 recv: 4 hello, world: 5 from 999
2022/03/18 12:56:51 recv: 4 hello, world: 5 from 111
2022/03/18 12:56:51 recv: 2 hello, world: 5 from 111
2022/03/18 12:56:51 recv: 0 hello, world: 5 from 111
2022/03/18 12:56:52 recv: 0 hello, world: 6 from 999
2022/03/18 12:56:52 recv: 0 hello, world: 6 from 111
2022/03/18 12:56:52 recv: 4 hello, world: 6 from 111
2022/03/18 12:56:52 recv: 2 hello, world: 6 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 999
2022/03/18 12:56:53 recv: 0 hello, world: 7 from 999
2022/03/18 12:56:53 recv: 4 hello, world: 7 from 999
2022/03/18 12:56:54 recv: 4 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 4 hello, world: 8 from 999
2022/03/18 12:56:54 recv: 4 stop
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 999
2022/03/18 12:56:54 recv: 2 stop
2022/03/18 12:56:54 recv: 0 hello, world: 8 from 111
2022/03/18 12:56:54 recv: 0 stop
ちょっと見難いので、"recv: 2"を、それぞれ異なる送信者ごとに整理してみます。
c:\Users\ebata\goga\1-7\test>grep "recv: 2" dummy.txt | grep 999
grep "recv: 2" dummy.txt | grep 111
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 111
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 111
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 111
2022/03/18 12:56:51 recv: 2 hello, world: 5 from 111
2022/03/18 12:56:52 recv: 2 hello, world: 6 from 111
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 111
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 111
2番がロストしています。
c:\Users\ebata\goga\1-7\test>grep "recv: 2" dummy.txt | grep 999
grep "recv: 2" dummy.txt | grep 999
2022/03/18 12:56:46 recv: 2 hello, world: 0 from 999
2022/03/18 12:56:47 recv: 2 hello, world: 1 from 999
2022/03/18 12:56:48 recv: 2 hello, world: 2 from 999
2022/03/18 12:56:49 recv: 2 hello, world: 3 from 999
2022/03/18 12:56:50 recv: 2 hello, world: 4 from 999
2022/03/18 12:56:53 recv: 2 hello, world: 7 from 999
2022/03/18 12:56:54 recv: 2 hello, world: 8 from 999
5、6番がロストしています。
「送信者を1人にすれば、問題が発生する可能性はなくなる」という仮説は成り立ちそうです。
考えてみれば、このプログラムでは、2つの送信者を区別する方法を入れていないのですから、当然かもしれません。
という訳で、送信者を1人にする方法の実装で、もうちょっとがんばってみたいと思います。
ただ、この方式では、送受信の際にロックをかけているので、受信者の数が膨大になれば、プログラム全体のパフォーマンスが劣化する可能性があります。
リアルタイム系のプログラムには、使わない方が良いかもしれません。
ただ、プログラムの動作状況を簡単に見たいようなケースでは、とても便利なので、当面は手放せないと思います。
以上
func main() {
ii := 10
queue := make(chan int, 100)
defer close(queue)
for i := 0; i < 5; i++ {
go worker(i, queue)
}
for {
//k := rand.Intn(1000)
for i := 0; i < 200; i++ {
queue <- ii
}
time.Sleep(1 * time.Second) // 2秒待つ
ii++
go worker(ii, queue)
}
}
func worker(i int, queue chan int) {
for j := range queue {
fmt.Println(i, ",", j)
}
}