teratail header banner
teratail header banner
質問するログイン新規登録

質問編集履歴

1

別案&やはりTimer使う羽目に・・・

2019/07/11 21:33

投稿

zonozono
zonozono

スコア16

title CHANGED
File without changes
body CHANGED
@@ -59,4 +59,121 @@
59
59
  reentryflg3 = false;
60
60
  }
61
61
 
62
- ```
62
+ ```
63
+
64
+ とりあえずまとめました。
65
+
66
+ ●私の案1
67
+ キューに積んだ後タイマA、Bイベントでキュー処理開始予約だけしてメソッド抜ける。
68
+ タイマイベントではキュー内各項目を読み上げ、キューが空になったら終了。
69
+ 読み上げのみ別スレッドで、読み上げ中はawaitでメッセージループに戻る。
70
+ タイマBイベントはタイマAイベントの最初のawait時に発生するので疑似マルチスレッド状態。
71
+ whileループもメインスレッドなので、Invoke使わず随時フォームとやり取りできる。
72
+
73
+ ●YAmaGNZ氏案
74
+ キューに積んだ後サブスレッドA、Bが動いて無かったら動かしメソッド抜ける。
75
+ サブスレッドではキュー内各項目を読み上げ、キューが空になったら終了。
76
+ 各項目の読み上げ開始をメインスレッドのフォームに表示させるにはInvokeでの通知必要。
77
+
78
+ ●一般的な?Producer-Consumerパターン案
79
+ メインスレッド開始時にサブスレッドA、Bも起動。
80
+ 以降メインスレッドはキューに積み続けるだけ。サブスレッドはBlockingCollection<>.Take()で待機してキューに入る度に読み上げの無限ループ。
81
+ 各項目の読み上げ開始をメインスレッドのフォームに表示させるにはInvokeでの通知必要。
82
+
83
+
84
+
85
+
86
+
87
+ その上で、下記の様なものを作ってみました。こんな頻繁に別スレッド建てたり潰したりするより、複数スレッド並列でInvokeなりでやり取りしろよって気もしますが、ほぼメインスレッドだけなので割と気に入ってます。
88
+
89
+ ●私の案2
90
+ メインスレッドのメッセージループを拡張する感じで、別スレッドでキュー待機し新着有ったら発動する処理を組み込んでおく。
91
+ 以降キューに積み続けるだけ。
92
+ ほぼメインスレッドなので、Invoke使わず随時フォームとやり取りできる。
93
+
94
+ ```c#.net
95
+ private void Form1_Shown(object sender, EventArgs e)
96
+ {
97
+ //StartQueLoopL(); //直接呼んだらStartQueLoopR()にたどり着かない
98
+ timer2.Interval = 1;
99
+ timer2.Start(); //タイマ使ってメッセージループからStartQueLoopL()呼ばせる
100
+ StartQueLoopR(); //こっちは直接呼んでもいいがプログラム終了までShown抜けられないってヤダ
101
+ }
102
+
103
+ private void timer2_Tick(object sender, EventArgs e)
104
+ {
105
+ timer2.Stop();
106
+ StartQueLoopL();
107
+ }
108
+
109
+ //データを貯めとくキュー
110
+ private BlockingCollection<string> bcCueL = new BlockingCollection<string>(10);
111
+ private BlockingCollection<string> bcCueR = new BlockingCollection<string>(10);
112
+
113
+ private async void StartQueLoopL()
114
+ {
115
+ //最初に一回呼ばれるだけなので再入管理省略
116
+
117
+ while (true)
118
+ {
119
+ //別スレッドでキュー待機、その間一旦メッセージループに戻る
120
+ string txt = await Task.Run<string>((Func<string>)bcCueL.Take);
121
+
122
+ textBoxL.Text = $"猫「{txt}」"; //フォームいじれる
123
+
124
+ //別スレッドで読み上げ、終わるまで一旦メッセージループに戻る
125
+ bool result = await Task.Run<bool>(()=>{return SpeakSubL(txt); }); //左スピーカーで読み上げ
126
+ textBoxL.Text = "";
127
+ }
128
+ }
129
+
130
+ private async void StartQueLoopR()
131
+ {
132
+ //最初に一回呼ばれるだけなので再入管理省略
133
+
134
+ while (true)
135
+ {
136
+ //別スレッドでキュー待機、その間一旦メッセージループに戻る
137
+ string txt = await Task.Run<string>((Func<string>)bcCueR.Take);
138
+
139
+ textBoxR.Text = $"人「{txt}」"; //フォームいじれる
140
+
141
+ //別スレッドで読み上げ、終わるまで一旦メッセージループに戻る
142
+ bool result = await Task.Run<bool>(()=>{return SpeakSubR(txt); }); //右スピーカーで読み上げ
143
+ textBoxR.Text = "";
144
+ }
145
+ }
146
+
147
+ private void button12_Click(object sender, EventArgs e)
148
+ {
149
+ string[] txtL = { "フシャーフシャー", "にゃーにゃー" }; //どこかから複数データ拾ってくる
150
+ foreach(string s in txtL)
151
+ {
152
+ bcCueL.TryAdd(s); //キューに入れる
153
+ }
154
+
155
+ string[] txtR = { "ほげほげ", "ちゅーるちゅーる" }; //どこかから複数データ拾ってくる
156
+ foreach(string s in txtR)
157
+ {
158
+ bcCueR.TryAdd(s);
159
+ }
160
+ }
161
+
162
+ ```
163
+
164
+ ただ、やはりTimerが必要になってしまいました。表題の件、いい方法ないですかね?
165
+ 要は、Control.Refresh()したら描画メッセージが積まれ、メソッド抜けた後描画イベントが実行されるのと同じような事をユーザー定義メッセージでしたいだけなのですが。
166
+
167
+ 調べても、「イベントを発生させるにはイベントハンドラを呼べ」とか「PerformClick()だ」とかばかりで・・・。下記1と2だと、クリックイベントの発生タイミングが違うんですが・・・。その場で呼びたいわけじゃなく、メソッド抜けた後メッセージループから呼ばせたいのです。
168
+
169
+ ```c#.net
170
+ //1
171
+ Thread.Sleep(10000); //この5秒目に(固まってる)ボタンを押す
172
+
173
+ //2
174
+ Thread.Sleep(5000);
175
+ button1.PerformClick();
176
+ Thread.Sleep(5000);
177
+ ```
178
+
179
+ NativeWindowクラス等使って無理やりやるしかないんですかね?