C#で、UIスレッド内で別スレッドを2つ以上起動するとUIスレッド上の処理時間が延びてしまう問題について質問です。
マルチスレッドを使用すると、UI上では別スレッドで処理が行われるため、並列処理を2つ以上呼び出してもUIスレッドのイベントの処理時間は
延びない想定でしたが、下記のコードを実行すると、イベント内の処理時間が20msecとか30msec以上延びることが時々あります。(0msecの時もある。)
この処理では、開始ボタン押下イベント(btnStart_Click)を実行すると、停止ボタン押下イベント(btn_Stop_Click)が実行されるまで
タイマーにセットした処理(ArrivedTimer)を2秒間隔で呼び出します。
ArrivedTimer中で呼ばれている関数(CalcCls.JikankakaruProc)は、処理時間が2.1秒程かかります。
ちなみに、ArrivedTimer関数内のt2のタスクの処理をコメントアウトすると、処理時間は0msecか1msecぐらいしか延びないです。
UIスレッド側の処理
C#
1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Diagnostics; 6using System.Drawing; 7using System.IO; 8using System.Linq; 9using System.Text; 10using System.Threading.Tasks; 11using System.Windows.Forms; 12using System.Timers; 13 14namespace Jikankakaru 15{ 16 public partial class Form1 : Form 17 { 18 /// <summary> 19 /// タイマークラス 20 /// </summary> 21 private System.Timers.Timer timerProc; 22 23 public Form1() 24 { 25 InitializeComponent(); 26 27 // 初期処理 28 InitData(); 29 } 30 31 /// <summary> 32 /// 初期処理 33 /// </summary> 34 private void InitData() 35 { 36 // 1秒に1回 37 this.timerProc = new System.Timers.Timer(); 38 // 間隔は1秒 39 this.timerProc.Interval = 2000; 40 // イベントセット 41 this.timerProc.Elapsed += ArrivedTimer; 42 } 43 44 #region ■デリゲート 45 46 /// <summary> 47 /// デリゲート 48 /// </summary> 49 /// <param name="str"></param> 50 private delegate void SetLabelDel(Label label, string str); 51 52 #endregion 53 54 #region ■デリゲート用関数 55 56 /// <summary> 57 /// デリゲート 58 /// </summary> 59 /// <param name="str"></param> 60 private void SetLabel(Label label, string str) 61 { 62 label.Text = str; 63 } 64 #endregion 65 66 /// <summary> 67 /// 処理開始 68 /// </summary> 69 /// <param name="sender"></param> 70 /// <param name="e"></param> 71 private void btnStart_Click(object sender, EventArgs e) 72 { 73 this.timerProc.Start(); 74 } 75 76 /// <summary> 77 /// タイマー停止 78 /// </summary> 79 /// <param name="sender"></param> 80 /// <param name="e"></param> 81 private void btn_Stop_Click(object sender, EventArgs e) 82 { 83 this.timerProc.Stop(); 84 } 85 86 /// <summary> 87 /// タイマーが来たときのイベント 88 /// </summary> 89 private void ArrivedTimer(object sender, ElapsedEventArgs e) 90 { 91 Stopwatch sw = new Stopwatch(); 92 93 BeginInvoke(new SetLabelDel(SetLabel), this.lblState, "未完了"); 94 95 // 冒頭の処理時間 96 sw.Start(); 97 98 // 時間かかる処理 99 OutDll ans1 = new OutDll(); 100 OutDll ans2 = new OutDll(); 101 102 Task t1 = Task.Run(() => CalcCls.JikankakaruProc(ans1)); 103 Task t2 = Task.Run(() => CalcCls.JikankakaruProc(ans2)); 104 105 int a1 = CalcCls.GetData(); 106 int a2 = CalcCls.GetData(); 107 108 // 答え出力 109 BeginInvoke(new SetLabelDel(SetLabel), this.lblAns1, a1.ToString()); 110 BeginInvoke(new SetLabelDel(SetLabel), this.lblAns2, a2.ToString()); 111 BeginInvoke(new SetLabelDel(SetLabel), this.lblState, "完了"); 112 113 sw.Stop(); 114 long data = sw.ElapsedMilliseconds; 115 sw.Reset(); 116 117 // 処理時間出力 118 BeginInvoke(new SetLabelDel(SetLabel), this.lblProctime, data.ToString()); 119 120 // 処理時間ファイル出力 121 WriteFile(data.ToString()); 122 } 123 124 /// <summary> 125 /// ファイル出力 126 /// </summary> 127 private void WriteFile(string str) 128 { 129 // 改行追加 130 str += Environment.NewLine; 131 132 // 追加形式 133 string filePath = this.txtFolder.Text + this.txtFile.Text + ".csv"; 134 135 Encoding enc = System.Text.Encoding.GetEncoding("shift_jis"); 136 137 // ファイル書き込み 138 File.AppendAllText(filePath, str, enc); 139 } 140 } 141} 142
タイマーイベント内で呼び出されている関数のクラス
C#
1/// <summary> 2 /// 計算用クラス 3 /// </summary> 4 public static class CalcCls 5 { 6 /// <summary> 7 /// 共通のデータ 8 /// </summary> 9 static private int commonData; 10 11 /// <summary> 12 /// 時間かかる処理 13 /// </summary> 14 public static void JikankakaruProc(OutDll ansData) 15 { 16 for (int i = 0; i < 1000000000; i++) 17 { 18 double data = 0; 19 data /= 100000; 20 } 21 22 // 共通データに乱数設定 23 System.Random r = new System.Random(1000); 24 commonData = r.Next(100); 25 ansData.FAns = commonData; 26 } 27 28 /// <summary> 29 /// データ返す 30 /// </summary> 31 /// <returns></returns> 32 public static int GetData() 33 { 34 return commonData; 35 } 36 }
実際の現場で使用するコードでは、100msec周期でカメラ画像取得イベントが呼び出されるため、20~30msecの遅れでもかなり致命的になります。
呼び出すタスクを2つにすると処理時間が延びてしまう原因が分かる方がおられましたら、宜しくお願い致します。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/26 11:57
2019/05/26 12:40
2019/05/26 14:02
2019/05/26 15:45
2019/05/26 16:22
2019/05/28 15:26