スレッドとタスク
スレッド
- 同時実行可能なスレッド数はプロセッサ数に依存する。
- .netではスレッドひとつに1MBのサイズが必要。
- スレッドの切り替えは高コスト。(コンテキストスイッチが発生する)
- 大量のスレッド作成はかえって逆効果。スレッドを作成するのではなく、タスクを生成したほうがよい。
タスク
- タスクはスレッドに割り当てられ実行される。このあたりの一連の管理(スレッド作成、タスク割当て)はスレッドプールで行なわれる。
- タスクの切り替えにはコンテキストスイッチが発生しないので、低コスト。
- スレッドプールは効率よく処理が行なえるようにスレッドの生成、タスクの割り当てを行なうしくみ。
動作確認コード
public class SomethingProcesser { public int Result { get; set; } public List<SomethingProcesser> Children { get; set; } public SomethingProcesser() { Children = new List<SomethingProcesser>(); Result = 0; } public void Proc() { _proc(this); } private void _proc(SomethingProcesser proc) { foreach (var child in proc.Children) { _proc(child); child.Proc(); } Thread.SpinWait(10000); } } public interface IProcesser { void DoProc(); } public class SomethingTask : IProcesser { private SomethingProcesser processer; public SomethingTask(SomethingProcesser processer) { this.processer = processer; } public void DoProc() { _doProc(0); } private void _doProc(int procIdx) { if (processer.Children.Count > procIdx) { Task task = new Task(() => { var p = processer.Children[procIdx]; _doProc(++procIdx); p.Proc(); }); task.Start(); task.Wait(); } } } public class SomethingThread : IProcesser { private SomethingProcesser processer; public SomethingThread(SomethingProcesser processer) { this.processer = processer; } public void DoProc() { _doProc(0); } private void _doProc(int procIdx) { if (processer.Children.Count > procIdx) { Thread task = new Thread(() => { var p = processer.Children[procIdx]; _doProc(++procIdx); p.Proc(); }); task.Start(); task.Join(); } } }
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private SomethingProcesser processer; private void Form1_Load(object sender, EventArgs e) { processer = new SomethingProcesser(); for (int i = 0; i < 100; i++) { var child = new SomethingProcesser(); processer.Children.Add(child); for (int j = 0; j < 100; j++) { child.Children.Add(new SomethingProcesser()); } } } private void button1_Click(object sender, EventArgs e) { var sw = new Stopwatch(); sw.Start(); processer.Proc(); sw.Stop(); Console.WriteLine("直列実行:{0}ms", sw.ElapsedMilliseconds); } private void button2_Click(object sender, EventArgs e) { //スレッド var sw = new Stopwatch(); var thread = new SomethingThread(processer); sw.Start(); thread.DoProc(); sw.Stop(); Console.WriteLine("スレッド使用:{0}ms", sw.ElapsedMilliseconds); } private void button3_Click(object sender, EventArgs e) { //タスク var sw = new Stopwatch(); var task = new SomethingTask(processer); sw.Start(); task.DoProc(); sw.Stop(); Console.WriteLine("タスク使用:{0}ms", sw.ElapsedMilliseconds); } }
実行結果
何度か実行したが、速度的には
1,タスク 2,スレッド 3,直列実行
の順番だった
メモリ使用率はどの方法も変わらず。
処理時間例)
直列実行:1782ms
タスク使用:917ms
スレッド使用:1397ms
1,タスク 2,スレッド 3,直列実行
の順番だった
メモリ使用率はどの方法も変わらず。
処理時間例)
直列実行:1782ms
タスク使用:917ms
スレッド使用:1397ms