スレッドセーフなコレクションクラス メモ
こういうクラスがあるのよということを箇条書きで。
System.Collections.Concurrentで定義されているクラスを使う。
System.Collections.Concurrent 名前空間 ()
System.Collections.Concurrent 名前空間 ()
BlockingCollectionクラス
- スレッドセーフなキューとして使用できる
- スレッドセーフなのでlock制御が必要ない
- TryTakeメソッドでDeQueueする
- AddメソッドでEnQueueする
- IsAddingCompletedプロパティでエンキュー側(プロデューサーというらしい)が追加しているかが確認できる。
スレッドとタスク
スレッド
- 同時実行可能なスレッド数はプロセッサ数に依存する。
- .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
正規表現クラスでの置換
お恥ずかしい話ですが、RegexクラスのReplaceメソッドがこんなに便利なんて知りませんでした。
正規表現で指定しなくてもMatchした最小単位で置換処理が行なわれるのですね。
//hello world を Hello Worldに置換する class Program { static void Main(string[] args) { var str = "hello world"; var regex = new Regex(@"(\W*)(\w+)"); Console.WriteLine(regex.Replace(str, (MatchEvaluator)delegate(Match m){ return m.Groups[1].Value + CultureInfo.CurrentCulture.TextInfo.ToTitleCase(m.Groups[2].Value);}) ); } }
プラットフォーム呼び出しによるデータのマーシャリング
まだ完全には理解できていない部分があります。下記参照元を参考にして
とりあえず動くようにはなりましたが、構造体文字列メンバー値が正しく取得できない等
一部おかしなところがあります。
とりあえず動くようにはなりましたが、構造体文字列メンバー値が正しく取得できない等
一部おかしなところがあります。
サンプルソース
統合アーカイバプロジェクト の 7-ZIP32.DLL が提供している機能を使用するサンプル//使用する機能分だけマーシャリングしている。 //オリジナルの定義情報は、7-ZIP32.DLL付属のヘッダーファイルなどを参照すること。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace MarshalingSample { //#define FNAME_MAX32 512 //typedef struct { // DWORD dwFileSize; // DWORD dwWriteSize; // char szSourceFileName[FNAME_MAX32 + 1]; // char dummy1[3]; // char szDestFileName[FNAME_MAX32 + 1]; // char dummy[3]; //} EXTRACTINGINFO, *LPEXTRACTINGINFO; [StructLayout(LayoutKind.Sequential)] public struct EXTRACTINGINFO { public UInt32 dwFileSize; public UInt32 dwWriteSize; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 513)] public char[] szSourceFileName; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 3)] public char[] dummy1; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 513)] public char[] szDestFileName; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 3)] public char[] dummy; } //typedef struct { // EXTRACTINGINFO exinfo; // DWORD dwCompressedSize; // DWORD dwCRC; // UINT uOSType; // WORD wRatio; // WORD wDate; // WORD wTime; // char szAttribute[8]; // char szMode[8]; //} EXTRACTINGINFOEX, *LPEXTRACTINGINFOEX; [StructLayout(LayoutKind.Sequential)] public struct EXTRACTINGINFOEX { public EXTRACTINGINFO info; public UInt32 dwCompressedSize; public UInt32 dwCRC; public UInt16 uOSType; public UInt32 wRatio; public UInt32 wDate; public UInt32 wTime; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 8)] public char[] szAttribute; [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.TBStr, SizeConst = 8)] public char[] szMode; } public class Zip { //typedef BOOL CALLBACK ARCHIVERPROC(HWND _hwnd, UINT _uMsg, UINT _nState, LPVOID _lpEis); public delegate bool ARCHIVERPROC(IntPtr _hwnd, UInt16 _uMsg, UInt16 _nState, IntPtr _ipEis); //int WINAPI SevenZip(const HWND _hwnd, LPCSTR _szCmdLine, LPSTR _szOutput, const DWORD _dwSize); [DllImport("7-zip32.dll", EntryPoint = "SevenZip")] public static extern int DeCompress(IntPtr _hwnd, string _szCmdLine, StringBuilder _szOutPut, UInt32 _dwSize); //BOOL WINAPI SevenZipSetOwnerWindowEx(HWND _hwnd, LPARCHIVERPROC _lpArcProc); [DllImport("7-zip32.dll", EntryPoint = "SevenZipSetOwnerWindowEx")] public static extern bool SetCallBack(IntPtr _hwnd, ARCHIVERPROC callback); } }
//7-ZIP32.DLL 機能を使用したサンプルアプリケーション //アプリケーションに*.zipファイルをドロップすると解凍を行うことができる //コールバック関数内で、解凍進捗をコンソールに出力している using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using System.Runtime.InteropServices; namespace MarshalingSample { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void MainForm_DragDrop(object sender, DragEventArgs e) { var dialog = new FolderBrowserDialog(); if (dialog.ShowDialog() != DialogResult.OK) return; var filename = ((String[])e.Data.GetData("FileDrop"))[0]; var cmd = "-hide e \"{zip}\" -o\"{out_path}\"".Replace("{zip}", filename); cmd = cmd.Replace("{out_path}", dialog.SelectedPath); var buffer = new StringBuilder(); Zip.SetCallBack(this.Handle, new Zip.ARCHIVERPROC(ProgressCallBack)); Zip.DeCompress(this.Handle, cmd, buffer, 0); } private bool ProgressCallBack(IntPtr _hwnd, UInt16 _uMsg, UInt16 _nState, IntPtr _ipEis) { EXTRACTINGINFOEX exinfo = (EXTRACTINGINFOEX)Marshal.PtrToStructure(_ipEis, typeof(EXTRACTINGINFOEX)); if (exinfo.info.dwFileSize > 0) Console.WriteLine("TotalSize:{0} CurrentSize:{1}", exinfo.info.dwFileSize, exinfo.info.dwWriteSize); return true; } private void MainForm_DragEnter(object sender, DragEventArgs e) { var filename = ((String[])e.Data.GetData("FileDrop"))[0]; if (Path.GetExtension(filename) == ".zip") e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.None; } } }
列挙処理可能なクラスの実装
列挙クラス定義
namespace EnumeratorSample { /// <summary> /// 数値範囲の列挙を行う2 /// </summary> class RangeEnumerable2 { private int from; private int count; private int step; /// <summary> /// 初期化 /// </summary> /// <param name="from"></param> /// <param name="count"></param> /// <param name="step"></param> public RangeEnumerable2(int from, int count, int step) { this.from = from; this.count = count; this.step = step; } /// <summary> /// 列挙子を返す /// </summary> /// <returns></returns> public IEnumerable<int> GetEnumerable() { for (int i = from; i < from + count; i+=step) { yield return i; } } } /// <summary> /// 数値範囲の列挙を行う /// </summary> class RangeEnumerable : IEnumerable<int> { /* * IEnumerable<T>の実装バージョン。 * このインターフェイスが非ジェネリックバージョンを継承しているので * すべて実装する場合はIEnumerator実装クラスが必要になってしまう。 * */ private int from; private int count; /// <summary> /// 初期化 /// </summary> /// <param name="from">開始値</param> /// <param name="count">カウントする数</param> public RangeEnumerable(int from, int count) { this.from = from; this.count = count; } #region IEnumerable<int> メンバ public IEnumerator<int> GetEnumerator() { for (int i = from; i < from + count; i++) { yield return i; } } #endregion #region IEnumerable メンバ /* * 異なるインターフェイス間で同名のメソッドが存在している場合の記述方法。詳細は下記参照。 * ・どのインターフェイス実装かをフルネームで書く。 * ・この場合はアクセス修飾子は書かない。(書くとビルドエラーになる) * *明示的なインターフェイスの実装 (C# プログラミング ガイド) * http://msdn.microsoft.com/ja-jp/library/ms173157.aspx */ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new EnumeratorSample.NonGenelic.RangeEnumrator(from, count); } #endregion } } namespace EnumeratorSample.NonGenelic { /* * IEnumeratorインターフェイス実装は下記を参照 * http://msdn.microsoft.com/ja-jp/library/system.collections.ienumerator.current%28VS.80%29.aspx * */ class RangeEnumrator : System.Collections.IEnumerator { private int[] array; private int idx; /// <summary> /// 初期化 /// </summary> /// <param name="from"></param> /// <param name="count"></param> public RangeEnumrator(int from, int count) { array = new int[count]; for (int i = 0; i < count; i++) array[i] = from + i; idx = -1; } #region IEnumerator メンバ public object Current { get { try { return array[idx]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } public bool MoveNext() { idx++; return idx < array.Length; } public void Reset() { idx = -1; } #endregion } }
上記列挙クラスの使用
/// <summary> /// Window1.xaml の相互作用ロジック /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { //列挙処理 foreach (var val in new RangeEnumerable(1, 10)) { Console.WriteLine("Value:{0}", val); } //非ジェネリックでの列挙処理 System.Collections.IEnumerable enumerable = (System.Collections.IEnumerable)new RangeEnumerable(11, 10); foreach (int val in enumerable) { Console.WriteLine("Value:{0}", val); } //IEnumerable<T>を返すメソッドでの列挙処理 foreach (var val in new RangeEnumerable2(20, 10, 3).GetEnumerable()) { Console.WriteLine("Value:{0}", val); } } }
C#3.0メモ1
主にLINQまわり1
3.0での省略記法について
データ型は型推論により決定される。
その為、省略して書かれていても型はしっかりと決まっている。
クラス定義の省略記法
public class Parson { public String Name { get; set; } public String Sex { get; set; } public String Belong { get; set; } } //この書き方は、ParsonがNewされた後に各項目へ値をセットする。 //その為、ReadOnlyなメンバなどにはセットできない。 Parson[] parsons = { new Parson(){Name = "山田太郎", Sex="男", Belong="営業"}, new Parson(){Name = "勝山次郎", Sex="男", Belong="営業"}, new Parson(){Name = "山中真奈美", Sex="女", Belong="技術"}, new Parson(){Name = "としこ", Sex="女", Belong="秘書"}, new Parson(){Name = "越中銀次郎", Sex="男", Belong="広報"}, new Parson(){Name = "大鳥昭信", Sex="男", Belong="社長"} };
LINQでの反復処理
Enumerableクラスで定義されているメソッド・IEnumerableクラスの拡張メソッドで、ごにょごにょすることが多い。
シンプルなクエリ
//select句には、クエリを実行したときに生成される値の型を指定します。 //省略した場合は、範囲変数と同じデータ型のシーケンスが返される。 //qのデータ型はIEnumerable<int> var q = from x in Enumerable.Range(1, 10) select x; foreach (var rec in q) Console.WriteLine(rec.ToString());
クエリ同士の結合
//商品分類 private class ItemKind { public int KindId { get; set; } public String KindName { get; set; } } //商品 private class Item { public int ItemId { get; set; } public int KindId { get; set; } public String ItemName { get; set; } public int ItemPrice { get; set; } } //クエリ実行 private class JoinQueryTest { private ItemKind[] kinds; private List<Item>items = new List<Item>(); private Random rmd; public JoinQueryTest() { rmd = new Random(); kinds = new ItemKind[] { new ItemKind() { KindId = 1, KindName = "分類1" }, new ItemKind() { KindId = 2, KindName = "分類2" }, new ItemKind() { KindId = 3, KindName = "分類3" }, new ItemKind() { KindId = 4, KindName = "分類4" }, }; for (int i = 0; i < 10000; i++) { items.Add(new Item(){ItemId = i + 1 , ItemName = "アイテム" + i.ToString() , ItemPrice = 10000 - i , KindId=rmd.Next(1,4) }); } } public void DoTasukigakeQuery() { var sw = new System.Diagnostics.Stopwatch(); //下記クエリのようなJoinしないたすきがけは非常に遅いので使用しないこと。 sw.Start(); var q1 = from x in kinds from y in items where x.KindId == y.KindId && x.KindId == 2 select new { Name = y.ItemName, Kind = x.KindName }; sw.Stop(); Console.WriteLine("Query1 Define Time:{0}", sw.ElapsedMilliseconds); sw.Reset(); sw.Start(); StringBuilder sb = new StringBuilder(); foreach (var rec in q1) sb.Append("Name:" + rec.Name + " Kind:" + rec.Kind.ToString()); sw.Stop(); Console.WriteLine("Query1 ResultOutputInfo Count:{0} Time:{1}", q1.Count(), sw.ElapsedMilliseconds); sw.Reset(); } public void DoJoinQuery() { var sw = new System.Diagnostics.Stopwatch(); sw.Start(); var q2 = from x in items join y in kinds on x.KindId equals y.KindId where x.KindId == 2 select new { Name = x.ItemName, Kind = y.KindName }; sw.Stop(); Console.WriteLine("Query2 Define Time:{0}", sw.ElapsedMilliseconds); sw.Reset(); sw.Start(); StringBuilder sb = new StringBuilder(); foreach (var rec in q2) sb.Append("Name:" + rec.Name + " Kind:" + rec.Kind.ToString()); sw.Stop(); Console.WriteLine("Query2 ResultOutputInfo Count:{0} Time:{1}", q2.Count(), sw.ElapsedMilliseconds); sw.Reset(); } } //[実行結果] //1回目 //Query1 Define Time:0 //Query1 ResultOutputInfo Count:3308 Time:14 //Query2 Define Time:2 //Query2 ResultOutputInfo Count:3308 Time:16 //2回目 //Query1 Define Time:0 //Query1 ResultOutputInfo Count:3354 Time:10 //Query2 Define Time:0 //Query2 ResultOutputInfo Count:3354 Time:2 //3回目 //Query1 Define Time:0 //Query1 ResultOutputInfo Count:3392 Time:8 //Query2 Define Time:0 //Query2 ResultOutputInfo Count:3392 Time:3 //4回目 //Query1 Define Time:0 //Query1 ResultOutputInfo Count:3325 Time:27 //Query2 Define Time:0 //Query2 ResultOutputInfo Count:3325 Time:3 //5回目 //Query1 Define Time:0 //Query1 ResultOutputInfo Count:3329 Time:14 //Query2 Define Time:0 //Query2 ResultOutputInfo Count:3329 Time:4