LINQは本当に強力だ (2) クエリの連結と拡張メソッド

小ネタだ(しかし、重要)。
書いたクエリはすぐには実行されず、列挙を行った時点(おおよそ、IEnumerable<T>.GetEnumerator()を呼び出した時点)で、実際にクエリが評価される。LINQメソッドの中には、その場で評価が実行されるものもある。例えば、ToList()がそうだ。

// 何の抽出かな?
int[] years = new int[] { ... };
IEnumerable<int> query =
    from year in years
    // letはクエリ内の一時変数のようなもの。
    // SQL Serverのクエリなら、同じ式を多量に記述してもクエリオプティマイザが検出して重複を取り除くが、
    // LINQ(LINQ to Object)はそこまでやらないので、letを使うと良い場合がある。
    // (ここではそのような深い意味はない)
    let mod4 = (year % 4) == 0
    let mod100 = (year % 100) == 0
    let mod400 = (year % 400) == 0
    where mod400 || (!mod100 && mod4)
    select year;

// クエリを実行してList<T>に格納する
List<int> leapYears = query.ToList();

ToList()の部分は、以下のような事をしている。

List<int> leapYears = new List<int>();
foreach (int value in query)
{
    leapYears.Add(value);
}

あるいは、もっとこう。

List<int> leapYears = new List<int>(query);

後者ならかなり短く記述出来るわけだが、ToList()を使う利点は、LINQのメソッド連結式の延長上で書けるからだ。例えば、

// 2000年より前のうるう年を昇順にしてList<int>に入れて返す
List<int> results =
    // メソッド連結式(Where()とOrderBy()はデリゲートで評価結果を得る。最後にToList()する)
    query.Where(delegate(value) { return value < 2000; }).
        OrderBy(delegate(value) { return value; }).
        ToList();

もちろん、ToList()のところを、new List<int>(…)と書いたって良いのだが、Visual Studio上でこれを書けば、何を言っているのか分かると思う。要するに「すらすら書ける」ということだ。

さて、「すらすら書く」ためには、LINQの標準メソッド群でも、やや不足を感じることがある。
例えば、ToDictionary()を使えば、Dictionary<T, U>に変換できるのだが、SortedDictionaryが欲しい場合には、ToSortedDictionary()なるものを期待しても、そういうメソッドはない。
某サイトでは、一旦ToDictionary()で辞書を生成した後、SortedDictionaryのコンストラクタに渡して生成する例が掲載されていた。しかし、それではDictionaryは完全に無駄だ。Dictionaryは内部でハッシュコード取得と分散を行っているはずなので、そのコストはドブに捨てる事になる。そして、やはりすらすらとは書けない。

そこで、無いものは作ってしまえ、と。

// staticクラスでなければならない
public static class CollectionExtensions
{
    // staticメソッドの第一引数にthisを付けることで、「拡張メソッド」となる。
    // Func<V, T>はジェネリックデリゲートで、V型引数を取り、T型を返すメソッドを受け入れる
    public static SortedDictionary<T, U> ToSortedDictionary<T, U, V>(
        this IEnumerable<V> enumerable,
        Func<V, T> keySelector, Func<V, U> valueSelector)
    {
        // 入れ物を作って...
        SortedDictionary<T, U> dictionary = new SortedDictionary<T, U>();

        // 列挙しながらセレクタを通してキーと値を取得して格納
        foreach (V entry in enumerable)
        {
            dictionary.Add(keySelector(entry), valueSelector(entry));
        }
        return dictionary;
    }
}

セレクタとして受け入れるデリゲートの部分は、.NET 2.0世代の知識では少し理解が厳しいかもしれない。まず、ToDictionary()がどのように使われるのかを確認しておく。

// 適当な数列
int[] values = new int[] { ...};
// 数列を文字列化したものとペアで保存する
Dictionary<int, string> results =
    // 条件式がなくても全く問題なし
    (from value in values
    // 匿名クラスを使って、一時的にクラスに値を保存する
     select new { Arg1 = value, Arg2 = value.ToString() }).
    // 匿名クラスから値を取り出してDictionaryにする。
    // "entry"は、式内でのみ有効な引数(上の匿名クラスのインスタンスが渡って来る)
    ToDictionary(delegate(entry) { return entry.Arg1; }, delegate(entry) { return entry.Arg2; });

匿名クラスというのは、名前の無い、初期化だけが出来るクラスの事で、new 文の後ろのブロックで読み取り専用フィールドを定義・生成出来る。上記ではArg1とArg2を宣言して、それぞれ値を代入した。
匿名クラスには名前がないので、コード上で型名を書くことが出来ない。しかし、上の式を見れば、巧妙にクラス名を避けている事が分かる(ここから、何故”var”が必要になったかが分かれば、良い洞察だ :-)
そして、今やToSortedDictionary()という武器が手に入ったので、以下のように書けばよい。

SortedDictionary<int, string> results =
    (from value in values
     select new { Arg1 = value, Arg2 = value.ToString() }).
    ToSortedDictionary(delegate(entry) { return entry.Arg1; }, delegate(entry) { return entry.Arg2; });

晴れて、イメージ通りのToSortedDictionary()が得られた。
しかし、私はToDictionary()にもやや納得が行かない部分があった。一旦匿名クラスに値を入れ、その後セレクタを使って再度辞書に入れ直すという部分だ。どうもスマートじゃない。
無い道具は作ってしまえ、と。

// 初めからKeyValuePairでいいじゃん?
public static SortedDictionary<T, U> ToSortedDictionary<T, U>(
    this IEnumerable<KeyValuePair<T, U>> enumerable)
{
    // 入れ物を作って...
    SortedDictionary<T, U> dictionary = new SortedDictionary<T, U>();

    // 列挙しながらそのまま格納
    foreach (KeyValuePair&lt;T, U&gt;  entry in enumerable)
    {
        dictionary.Add(entry.Key, entry.Value);
    }
    return dictionary;
}

そうすると、クエリ側にはKeyValuePairを書かなければならなくなる。これをどう捉えるかだが、直後に辞書に入れると分かっているなら、KeyValuePairで生成した方がすっきりするのが私流。

SortedDictionary<int, string> results =
    (from value in values
     select new KeyValuePair<int, string>(value, value.ToString())).
    ToSortedDictionary();

言わずもがな、ToDictionary()にも同じものが作れるよね?
し・か・も! この拡張メソッドはIEnumerable<KeyValuePair<T, U>>を実装する、全てのクラスで使えるのだ。それはつまり、Dictionary<T, U>とSortedDictionary<T, U>そのものだ。

Dictionary<int, string> hashed = new Dictionary<int, string>();

// .. 辞書にいろいろ追加

// DictionaryをSortedDictionaryに変換
SortedDictionary<int, string> sorted = hashed.ToSortedDictionary();

// SortedDictionaryをDictionaryに変換
Dictionary<int, string> dictionary = sorted.ToDictionary();

もちろん、辞書じゃなくてもOKだ。

// IEnumerable<KeyValuePair<T, U>>を実装していればいいのだから...
List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

// ..

// ListをSortedDictionaryに変換
SortedDictionary<int, string> sorted = list.ToSortedDictionary();

// ListをDictionaryに変換
Dictionary<int, string> dictionary = list.ToDictionary();

なんだか、色々な事に使えそうな気がして来ないだろうか?
つづく

LINQは本当に強力だ (1) データ加工の究極の道具

長い間、.NET2.0から知識をアップグレードしていなかったのだが、先日一気に.NET4.0の知識を詰め込んだ。
LINQに触る必要性から、匿名デリゲート・ラムダ式・匿名クラス・式ツリー・拡張メソッドなどを覚えたのだが、はっきり言って今まで勉強を放置してきた事に、激しく後悔している。
「食わず嫌い」だったのは、矢継ぎ早に追加される新しい構文に対する抵抗だったような気がする。C++のテンプレート拡張がもめたらしいのも、気持ちはよくわかる。

テンプレートもそうだが、LINQも言語思想の革命と言っても言い過ぎではないぐらいのインパクトがあった。

LINQの何が良いかと言えば、比類なき拡張性だろう。これを支えているのはIEnumerable<T>インターフェイスと、拡張メソッド構文なわけだが、LINQに触ったことが無い人向けに、興味が持てそうな事を書いてみる(但し無保証 :-) まぁ、もう既に「枯れた」技術になり始めてる気がするので、今更だが)

IEnumerable<T>インターフェイスは、List<T>ジェネリッククラスや配列が実装している。というよりも、「列挙」可能なクラスには、このインターフェイスが実装されている(ちなみに、非ジェネリックなIEnumerableは列挙可能だが、LINQで使うには難点があるので省略)。

このインターフェイスが実装されていれば、以下のようにforeach出来る。

List<string> nameList = new List<string>();
// ...
foreach (string name in nameList)
{
    // 様で呼んでくれなきゃやだ
    if (name.EndsWith("様") == true)
    {
        Console.WriteLine(name);
    }
}

# varはわざと使ってないよ?(一応、.NET 2.0技術者向け)
で、LINQでforeachの部分を以下のように書ける。

// LINQクエリ(まだ実行されない)
IEnumerable<string> kingNames =
    // nameListの個々の要素をnameとして取り出し、
    from name in nameList
    // nameの終端が"様"の場合に、
    where name.EndsWith("様") == true
    // このクエリの結果要素としてnameを返す。
    select name;

// クエリの結果を列挙
foreach (string name in kingNames)
{
    Console.WriteLine(name);
}

「仮」に、nameListの要素数が非常に多かったとする(100万件とか)。
その場合に、以下のように「AsParallel()メソッドを呼び出す」だけで、”様”付きの判定をマルチスレッド化出来る。

IEnumerable kingNames =
    from name in nameList.AsParallel()
    where name.EndsWith("様") == true
    select name;

大量の要素の、しかしながら個々の要素の判定にはそれほどの判定コストを必要としない場合でも、この「PLINQ(パラレルLINQ)」は効率的に動作するはずだ。

通常、マルチスレッドで同じことを行う場合、以下のようなコードとなる。

private readonly List<T> kingNames = new List<T>();
private int count = 1;
private readonly ManualResetEvent final = new ManualResetEvent(false);

// ワークアイテム実行のエントリポイント
private void WorkItemEntry(object argument)
{
    string name = (string)argument;
    if (name.EndsWith("様") == true)
    {
        // 追加が競合しないように、コレクションをロックする
        lock (kingNames)
        {
            kingNames.Add(name);
        }
    }

    // 自分が最後なら通知
    if (Interlocked.Decrement(ref count) == 0)
    {
        final.Set();
    }
}

public IEnumerable<T> GetKingNames()
{
    List<string> nameList = new List<string>();

    // ...

    // ワークアイテムをキューに入れる
    foreach (string name in nameList)
    {
        Interlocked.Increment(ref count);
        ThreadPool.QueueUserWorkItem(WorkItemEntry, name);
    }

    // (キューに入れている間に終わってしまわないように、カウンタの初期値を1にしてあるので減算)
    if (Interlocked.Decrement(ref count) == 0)
    {
        final.Set();
    }

    // 全てのスレッドが終わるのを待つ
    final.WaitOne();
    return kingNames;
}

まず、第一にわかることは、「マルチスレッド対応」にするだけで面倒で不安な同期処理を大量に記述しなければならない事だ。この処理の肝は「EndsWith(“様”)」だけであるのに、付随コードの何とも多い事か。そして、これだけでもLINQで書く事がいかに強力であるかが分かる。

LINQで書く場合、「AsParallel()」で列挙子を修飾するだけだ。AsParallelメソッドは、指定された列挙子(nameList)をParallelQuery<T>クラスでラップする。LINQの他のメソッド同様、これだけではまだクエリは実行されていない。最後にkingNamesをforeachで列挙するまで、全く処理は行われていない。つまり、中間バッファは一切不要という点も重要だ。

元のデータが100万件、EndsWithで絞り込んでも50万件のデータがあるとすれば、判定しては新たなコレクション(中間バッファ)にデータを格納するのはためらわれる。また、列挙した時点で一気に処理を実行することで、CPUのキャッシュにコードが維持される可能性も高まる。おまけに.NETはJITコンパイラで動いているので尚更だ。

次に、レガシーコードで示した方法には、マルチスレッドを効率的に実行できない罠がある。それは、kingNamesに対してロックを行っている部分だ。この実装は、一回のワークアイテム実行に占めるロック時間が、相対的に長すぎる。そのため、複数のスレッドで同時実行されると、ロック競合が多量に発生する。結局その間は「シングルスレッド」と変わらないのだ。おまけにスレッドコンテキストの遷移に時間がかかってしまうので、シングルスレッド性能より落ちてしまう。

「既存コードのマルチスレッド化でパフォーマンス向上」なんて、生易しい事ではないのだ。

それがもっとよく分かるように、このレガシーコードを改良してみる。

// スレッドのエントリポイント
private void ThreadEntry(object argument)
{
    KeyValuePair<Queue<string>, List<string>> pair = (KeyValuePair<Queue<string>, List<string>>)argument;
    Queue<string> queue = pair.Key;
    List<string> localKingNames = pair.Value;

    while (true)
    {
        // キューから文字列を取り出す。最後なら抜けてスレッド終了
        string name = queue.Dequeue();
        if (name == null)
        {
            return;
        }
        if (name.EndsWith("様") == true)
        {
            // コレクションはスレッド固有なのでロック不要
            localKingNames.Add(name);
        }
    }
}

public IEnumerable<T> GetKingNames()
{
    List<string> nameList = new List<string>();

    // ...

    // スレッドに供給する文字列と、結果文字列を格納するコレクションをスレッド数分用意する
    List<KeyValuePair<Queue<string>, List<string>>> pairs = new List<KeyValuePair<Queue<string>, List<string>>>();
    for (int i = 0; i < Environment.ProcessorCount; i++)
    {
        pairs.Add(KeyValuePair<Queue<string>, List<string>>(
            new Queue<string>(), new List<string>());
    }

    // 事前にキューに入れる(スレッド毎に均等に)
    int index = 0;
    foreach (string name in nameList)
    {
        pairs[index].Key.Enqueue(name);
        index = (index + 1) % Environment.ProcessorCount;
    }

    // スレッドを生成して実行を開始する
    List<Thread> threads = new List<Thread>();
    for (int i = 0; i &lt; Environment.ProcessorCount; i++)
    {
        Thread thread = new Thread(ThreadEntry);
        threads.Add(thread);
        thread.Start(pairs[i]);
    }

    // スレッド群が終わるのを待つ
    List<string> kingNames = new List<string>();
    for (int i = 0; i < Environment.ProcessorCount; i++)
    {
        threads[i].Join();
        // 終わったスレッドから、結果を収集する
        kingNames.AddRange(pairs[i].Value);
    }
    return kingNames;
}

あーもう、書いている矢先から面倒で、記事自体無かったことにしようかと5回ぐらい思った :-) (そんな訳で、コンパイルして検証はしていない)

要するにこのコードは、ロックを行わなくて済むように、事前にスレッド毎にデータを分散し、スレッド毎に結果を格納し、その結果はメインスレッド側で収集する、という事をしている。これはマルチスレッドでパフォーマンスを向上させる定石のようなものだ(ロックを不要にする)。キューにデータを入れ直している時点でメモリを余分に使っているため、さらなる改良が必要だが、「もういいだろう」。それにこれ以上書きたくない。

つまり、こういう面倒なことを、ParallelQuery<T>クラスの内部でやってくれるという事だ。そして、現在のCPU事情と言えば、シングルスレッド性能は頭打ちで、マルチコア・SMTを推進している。コードを高速化させるためには、マルチスレッドに対応させるコーディングを行う必要があるが、同期処理は面倒かつバグを生みやすい。上記のような短いコードでさえ、書いただけでは正しいかどうか分からない。

この最適化手法を知っている人なら、これがAsParallelするだけで実現される、なおかつそれはコンパイラが何か怪しげなことをやっているのではなく、全てライブラリだけで実現されていると聞けば、カルチャーショックを受けるはずだ(受けなきゃおかしい)。

#LINQのクエリ構文はコンパイラが解釈する。しかしParallelQuery<T>クラスは種も仕掛けもない、普通のライブラリだ。
#その気になれば、だれでも同じものが作れる。もちろん、安全に実行出来るようにするには高度な技術が必要なので、「俺ParallelQuery<T>」は作らない方がいい。

さて、どのようなコードでも、LINQで書かなければならない訳ではない。例えば、LINQで列挙しながら、列挙中のコレクションを更新するという操作はNGだ。しかし、それはLINQを使わない場合でも同じ事だ(LINQで記述すると、あまりに簡便になるため、出来ない事があると欠点に思えてくるが、それは多分違うよ?)。

また、LINQは「常に構造が変化する要素」を扱うのが難しい。これは当たり前だ。列挙する要素がintになったりstringになったりする状況では、クエリが簡便に記述出来ない(そういう状況が、IEnumerable非ジェネリックインターフェイスを使った場合だ。もっとも、LINQのライブラリはIEnumerableに対応していない。擬似的にIEnumerable<object>で試せば分かる)。

これを以って、やはりLINQは中途半端と思うなら、実用的なコードをLINQで書いてみるべきだ。また、LINQを使うのに、SQL Serverにアクセスする必要などない。IEnumerable<T>インターフェイスを実装していれば、あらゆる要素に応用が可能だ。

「LINQが適している分野ではLINQで書け」

これに尽きる。そしてLINQの「後」でPLINQが生み出されたように、LINQの拡張性にも目を見張るものがある。次回に続く。

COMのアパートメント (5) CoInitializeExは初期化ではない

さて、そろそろ架空のコードでは現実とどう違うのかわからなくなってくるので、現実のコードがどうなるのかを見ていくことにする。
COMで最初にお世話になるのは、CoInitializeとCoInitializeEx、CoUninitialize APIだ。
前回までの解説からなんとなくイメージを持ってもらえたかも知れないが、これらのAPIは、現在のスレッドにアパートメント属性を設定・解除する。
CoInitializeはCoInitializeExのバリアントなので、CoInitializeExで説明する。
CoInitailizeEx API

HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);

// COMに関する様々な処理...

CoUninitialize();

CoinitializeExの第一引数は使われていないので0(ないしNULL)を指定する。
第二引数は、初期化フラグを指定し、COINIT列挙値の値を使用する。ほとんどの場合、ここにはCOINIT_MULTITHREADEDかCOINIT_APARTMENTTHREADEDのどちらかを指定する。
COINIT_MULTITHREADEDにした場合、現在のスレッドのアパートメント属性として、MTA(マルチスレッドアパートメント)に属するように設定する。
COINIT_APARTMENTTHREADEDにした場合、STA(シングルスレッドアパートメント)に属するように設定する(そして、CoInitializeを使用すると、STA固定となる)。

マルチスレッドアパートメントに設定すると、そのスレッドは、COMコンポーネントとのやり取りで発生するすべてのスレッド同期作業を、自分で行うと宣言したことになる。
シングルスレッドアパートメントに設定すると、そのスレッドは、COMコンポーネントとのやり取りで発生するすべてのスレッド同期作業を、COMが背後で面倒見てほしいと宣言したことになる。

これらの違いについては次回に譲る。

これでCOMに関する下地が出来た訳だが、巷のコードを見ているとアパートメント選択以前の勘違いが多いことに気が付く。つまり、このAPIの呼び出しは、呼び出し元のスレッド(カレントスレッド)に対してアパートメントの設定を行うのであるが、まるでプロセスワイドにCOMの初期化が行われているかのような使い方だ。

たとえば、DLLによるライブラリを作成していたとする。このDLLは外部に以下のような公開エントリポイントを持つとする。

__declspec(dllexport) HRESULT __stdcall InitializeLibrary();
__declspec(dllexport) HRESULT __stdcall DoSpecialMakeMoney(const ULONG multiply);
__declspec(dllexport) HRESULT __stdcall TerminateLibrary();

このライブラリを初期化するには、InitializeLibraryを呼び出す。内部の実装(DoSpecialMakeMoney)では別のCOMコンポーネントを呼び出す予定なので、InitializeLibrary内でCoInitializeExを呼び出している。しかし、InitializeLibraryを呼び出したスレッドと、DoSpecialMakeMoneyを呼び出したスレッドは別のスレッドかも知れない。同じく、TerminateLibraryもまた、別のスレッドである可能性がある。

すると、CoInitializeExやCoUninitializeを呼び出す事は何の意味をも持たなくなってしまう(CoInitializeExは、「現在のスレッド」にアパートメントを「設定」するのだから)。

それどころか、InitializeLibraryを呼び出した時点で、すでにCOMの初期化が行われているかもしれない。しかもその時点のスレッドは、STAであるべきか、MTAであるべきかは、呼び出し元のみぞ知るわけであり、ライブラリの中では知りようがない(無理やり変更を試みると、RPC_E_CHANGED_MODEが返される)。

仮に、InitializeLibrary内でCoInitializeExのエラーコードを適切にハンドリングしたとする(RPC_E_CHANGED_MODEのほかにS_FALSEが返される可能性がある。これはすでに同じアパートメント属性で初期化されていることを示す)。これでエラーは回避できるかもしれないが、そもそも呼び出し元のスレッドはCOMの初期化を行っておらず、InitializeLibrary呼び出しの「後」で、自力でCoInitializeExを呼び出していたとしたらどうだろうか。その実装には関与できないかもしれず、従ってここで示したようなエラーの回避が行われているかどうかわからず、結局のところ、必要とされる正しいアパートメント属性に設定できたのかどうかもあやふやだ。

つまり、このようなコードは完全な「お手付き」と言える。最も、COMの初期化を行ってあげようという親切心は分からなくもないが…

堅牢なライブラリに仕上げるためには、まず、独自にCoInitializeEx(とCoUninitialize)の呼び出す事を止めることだ。また、CoInitializeExが呼び出されていない場合は、DoSpecialMakeMoneyの呼び出しに対して、COMの初期化が行われていない事を示すCO_E_NOTINITIALIZEDを返す(あるいはカスタムのエラーコードや、C++かつ環境限定なら例外をスローしても良い)。

CoInitializeExが呼び出されているかどうかは、COMの何らかのAPIを呼び出せばわかる。つまり、DoSpecialMakeMoneyの内部で実際にCOM APIを使用した時点でCO_E_NOTINITIALIZEDを検出し、異常処理を行えばよい。
また、呼び出し元のコードを記述する際は、スレッドを生成したら、速やかにCoInitializeExでアパートメント属性を確定しなければならない。生成したスレッドがどのアパートメントに属するのが適切なのかは、スレッドを生成した者にしかわからないからだ。

#筆者は過去にこのような不適切な実装を行っている、サードパーティ製のコンポーネントを目撃した。
#実際に業務に使用していたため、非常に困り、最終的にはサードパーティに「直させた」経験があるが、問題が解消するまでに、結局3か月近く掛かった…
#(技術的な問題点をお客様に理解してもらい、さらにサードパーティベンダーにも周知してもらい、修正され、解消されたことを確認するまでに掛かった時間)
#ベンダーは技術の内容をよく理解して使ってほしいと思う。

.NETのメインエントリポイントが、[STAThread]や[MTAThread]属性を使ってアパートメントを簡単に指定できるようにしているのは、このような理由によるものと推測している。実際、Windows FormsやWPFアプリケーションをウィザードで生成すると、自動的に[STAThread]が適用される。

勿論、属性を指定していなくても、Thread.CurrentThread.SetApartmentState()を呼び出せば設定できる(内部ではおそらくCoInitializeExを呼び出しているのであろう)が、アパートメント初期化のベストプラクティスは、スレッド生成と同時に設定してしまうというものだ。


最新のCOM記事:

COMについて、ローカルディスカッションで大幅に拡充してまとめ上げた記事があるので、そちらもどうぞ:「ChalkTalk CLR – COMのすべて」

コードコントラクトと検証

.NET Framework 4 より、本格的にコードコントラクトがサポートされるようになったそうだ。
とはいっても、実際に検証するのは別のツールであって、コードにコントラクトが書けるように、標準のインターフェイスが準備されたことだ。

System.Diagnostics.Contracts名前空間以下のクラスを使用する。

public int Divide(int a, int b)
{
    // 事前条件
    Contract.Requires(b != 0);

<pre><code>return a / b;
</code></pre>

}

要するに、Debug.Assert() していたような所をこれに置き換えていく。事後条件の場合は Contract.Ensures() を使う。戻り値をチェックしたい場合は、Contract.Result() で戻り値が参照出来る。
例えば、負の数が許されない加算であれば、

public int Add(int a, int b)
{
    // 事前条件
    Contract.Requires(a &gt;= 0);
    Contract.Requires(b &gt;= 0);

<pre><code>// 事後条件
Contract.Ensures(Contract.Result() &amp;gt;= 0);

return a - b;
</code></pre>

}

という具合だ。

この時点で、「非常に魔術的」な香りが漂っている。特に、事後条件の Contract.Result() で戻り値が参照出来るだとか、そもそも事後条件が除算処理の手前にあったりして、本当にこれでいいのか?という感じがする。
実は、最初にインターフェイスが準備されただけ、と書いたのはこの部分の事で、このコントラクト定義を実際に使うには以下の条件を満たす必要がある。

・コンパイル時シンボル「CONTRACTS_FULL」が定義されていないと、Contractクラスのメソッド呼び出しは全て削除され、バイナリには一切含まれない。
・コントラクトの検証は、能動的には行われない。Debug.Assert() のように、書いた場所で直にコードが確認するわけではない。
 実際には一部の評価は行われるが、結局のところは「バイナリリライター」と呼ばれるライブラリが介在する必要がある。

で、それでは、Debug.Assert() と比べて何が嬉しいのか?という話になるのだが(ようやく本題)、コードに書かれたコントラクトをメタデータ(とMSIL?)から抽出して、その情報を元にごにょごにょ出来るようになるというところが違う。
Microsoftの実装では、「Pex」というプロジェクトでコントラクトを利用している。Pexは、ユニットテストコードを自動的に生成するツールとライブラリで、コードからコントラクト定義を抽出して、事前・事後条件などからユニットテストが実行すべきテストパターンを計算、「テストコード」を吐く。

恐ろしいww

いや、もちろん、完全な自動化テストは不可能ということは分かっているのだが、特にこの場合はどれだけコントラクト定義が綿密に書けるかということと、いかに普段からテスト可能コードを書けるかと言う事に尽きるのだが、それにしてもとうとうやっちまったかという感じがする。
PexはVS2010のProfessional以上で使用可能で、MSDNサブスクライバダウンロードから入手可能。また、Ultimateでは標準で入っているので、Microsoftとしてももはやこのプロジェクトは実験段階を超えていると判断しているようだ。

より詳しく知りたい場合は、以下のサイトを参照されたし。

MSDNマガジン:クラスにソフトウェア コントラクトを導入する
MSDNマガジン:Visual Studio 2010 における Code Contracts の設定
Microsoft Reserch: Pex and Moles (なにげにMolesも凄いのだが)
VS2010 後の世代のプログラミング (2) Pex

他にも、コントラクト定義を使ったこんなのもある。

Contractor.NET | A .NET validation and specification strengthening tool

.NET Framework 4 未満の互換ライブラリを書いているのであれば、ここにある前身バージョンを使えば、コンパイルは通るようになります。
(.NET Framework 2 からOK。ただし、バイナリリライターはないので、検証は出来ません)

DevLabs: Code Contracts

Windows 8 WDK Preview (Eliminated BUILD.EXE !!)

遂に、WDKはVisual Studioに統合されるようだ :-)
というのも、Preview WDKをインストールしてみたのだが、「build.exe」が無い!!!

いや、ちょっと困る orz とりあえず既存のドライバをビルドしてみたかったのだが、そのままでは出来ない。
しかし、その代わりに msbuild の拡張アセンブリと思わしきファイルや、VSの拡張ファイルなどがある。
OSRでもその手の話題が出始めている(真面目に見てないのでわからないが)。

問題は、ドキュメントが無い事だ。どう料理していいのかわからない。
Previewだからこんなもんか。

Visual Studioに統合されると言っても、VS2011 向けだ。
気になるのは、2000はアウトとして、XPはどうだろう?というところかな。
Windows 2000はすでにWDK6001で終わってしまっている。
今は仕方がないので、2000向けだけ6001で、あとは7000でビルドしている。
この方法は、良くも悪くもbuild.exeがあるから使えるわけだが、build.exeが綺麗さっぱりなくなるのであれば、いよいよ2000のフェードアウトを考えなければならないか…

うーん、1ビルド分は、build.exeシミュレータみたいなものを用意して欲しかった。

ConnectでWDKの悪口突っ込んでたから、今更そんなこと言えないwww

あと、残念な点は、ARM関係のファイルは入ってなかったってことだ :-)
これまた、とりあえずクロスコンパイラでビルドとかやってみたかったのだが…

ユーザーインターフェイスとシステム雑記だらだら

Windows 8(仮称)が発表され、MSDNサブスクライバダウンロードからプレビュー版をダウンロードできるようだ。

#今ダウンロード中。
#Akamaiのダウンローダも少しはまともになって、キューに入れたダウンロードタスクの殆どは
#同時並行でダウンロードするようになったようだ

Windows 8でMetroインターフェイスが導入されるのだが、心配している点がある。

Windows 2.1 (!) で「プログラムマネージャ」と「ファイルマネージャ」が導入された。
この時に「なんてダサいインターフェイスだ」と思った。
DOS世代のアプリケーションランチャーとファイラーをそのまま実装しただけだった。
Finderの足元にも及ばなかった。正直、「これが未来か!」という気は全くしなかったものだ。

なんという名前だったか忘れたが、Finderもどきのユーティリティがあったりしたが
結局純正ではなかったので、広まることもなかった(詰めも甘かった)。

DOS5で導入された「DOSシェル」も、結局は薄皮かぶったドブネズミ状態だったわけで、
もっと根本からリソースを共有するための、優れた手段を提供しなけれなならないのにと思っていた。
UNIXはその点では優れていたが、現代のUNIXクローンもまたそこから脱することは出来なかった。

#未だにshとcshの延長でしかないし。
#その世界で完結する作業をしている分にはすばらしいが、
#多様化するユーザーインターフェイスを一元的に扱う機能は提供されていない。
#シェルの強みはパイプラインとジョブコントロールだと思っているので、
#それを超える操作は、独自ライブラリ・独自APIの世界にならざるを得ず、
#突然相互運用性と応用性を失う。

私がどうもAndroidにもiOSにも萌えないのは、あのメニューがプログラムマネージャに「クリソツ」だからかもしれない。
(見た目はきれいだけど)

Windows 95で「エクスプローラ」が導入された。
ファイルセントリックで、コンテキストメニューで操作を提示できる点は進んだと思う。
しかし、「デスクトップ」と「マイドキュメント」の概念がダメダメだ。
ツリーでディスクとフォルダ構成を見せている以上、内部の構造がそのまま素直に見えるようにすべきだったと思う。

あれがひっくり返っている(デスクトップがすべての起点にある)のが諸悪の原因だ。
技術者ではないユーザーは、あれのためにコンピューターの中のイメージが持てなくなってしまった。

「ここのデスクトップは、実はこのフォルダの中のここが見えているんですよ」
「???」

何回こういう説明をしただろうか…
未だに、マイドキュメントの位置をDドライブに移動するという説明を理解してもらえないことが多い。
(メーカー製のPCはせっかくパーティションがわけてあっても殆ど無意味なのだ)

ユーザーの学習曲線について考える責任者がいなかったのだろうか、或いは分かっていてやっているのか。
デジタルディバイドという言葉があまりはやらなくなったが、「デジタル音痴」はいまだ多数を占める。
こういう層の人ほど、コンピューターの応用方法を早く習得できる必要があるのだ。
そのためには、ただでさえ分かりにくい「コンピューター」を、更に理解不能にする要素は排除する必要がある。

UNIXのシェルは多少ロジカルな思考が必要だが、一度理解すれば応用力はどんどん伸びていく。
Windows環境ではこういう経験はほとんどない。環境どころかAPIも一貫性が無いおかげで苦労する。
そしてGUIの世界が一般的になった時、UNIXクローンも同じような問題を抱えるようになった。
(向こうはもっと激しいよね。コミュニティがどんどんAPIを変更していくから、ついていく気が無いと難しいし、
基本的にデベロッパ向けだから一般人は知る事すらかなわない)

Windows Phone 7.5のMetroインターフェイスには少し期待している。

「一から作った」ということは、過去のWindowsの文化を全て捨てて考えることが出来たと言う事だ。
iOSのおかげ(?)で、本来ならMS Labs内で終わるようなプロジェクトにゴーサインが出たわけだ。

Peopleハブは正に、「Peopleセントリック」ということだろう。
新しいデバイスであるからこそ、既存のデバイスを打ち破る「文化」が無いと魅力を感じられない。
進化が無いなら、今までの環境で十分。新しいデバイスに投資するなら、それだけの価値がないとね。
(価値は個人によるので、他人はどう感じるかは別の問題。見た目カッコいい!で選ぶのもアリだろう)

Windows Phoneという小さいデバイスであまり夢を見ることもできないが、いっそ「メタセントリック」を扱えるように出来れば、
OSの応用性に未来が見い出せるのではないかと思う。
Windowsのメインプロダクトが目指してほしいところはここだ。
だが、Windowsのメインプロダクトは過去を捨てることが出来ない宿命にある。
Metroインターフェイスを導入すると言っても、それはつまりは「新しいプログラムマネージャ」ではないかという気がする。

ラップインターフェイスの成功例として、.NET Frameworkは良くできていると思う。
周りからの色々な圧力(?)に屈せず、よくあそこまでの完成度にたどり着いた。
しかし、当初検討されていた、Windows自体を.NET Frameworkで再構築するという大胆な案は、もう実現しそうにない。
それが本当に良い事かどうかも分からないが、マイクロソフトがその案をフェードアウトしたということは、
夢は見れたかもしれないが、やはりそれだけでは「商売」にならないということだろう。
それはつまり、まだしばらくはWin32とWDMからは逃れられないと言う事だ。
そうなると、新しい機能はすべからく「ラップインターフェイス」となる。

Windows 8のMetroが、「羊の皮をかぶったドブネズミ」でなければいいのだが。

#インターフェイスが「切り替わる」という点で、もうすでにアレ感が漂っている…

Windows 8はともかく、Windows Phone 7.5で「儲ける」のはまだ先かな。
でも、今のうちにかじっておくと幸せになれるかもしれない。アイデアがあれば!

#シェルと言えば、PowerShellを必要に迫られて多少弄ったが、どうしても「できそこない」感がぬぐえない…
#パイプラインが、テキストストリームから卒業した点は評価している。
#.NET Frameworkがもっと早く完成していて、同時にPowerShellがリリースされ、
#標準のシェルがPSになっていれば、もうちょっと印象がちがっていたかもしれない。
#でも、シェルコマンドのライブラリ作成がスマートじゃない。なんか異端な感じがするんだよな。
#よく頑張ったとは思うけどね。
#そういう印象は、Exchangeのコマンドレットを見ると特に強く感じる。
#PS使います・コマンドレットです、って言うだけで、応用性無いじゃん。
#あんな大量のコマンドレット、専門職じゃないし使えないって。バッチファイルと何が違うのさ?w

新しいVAIO Z

ソニー「VAIO Z VPCZ21AJ」

うーん。どうかなぁ。

そもそも高速GPU欲しい人が、「分離した」ノートを欲しいと思うだろうか。
それなら最初からパワフルなデスクトップと薄型ノートの方が使い出と自由度があるだろうし、出先でGPU欲しい人は結局GPU内臓ノートを買うのでは?

#光リンクでPCI Expressのブリッジが実現できたとか、技術的なチャレンジとしては
#面白いかもしれないけど。でも、「分離できます」よりも「内蔵できませんでした」
#に思えてしまう。
#売れるつもりで作ったんだよね? :-P
#いずれにしても、何とかタイマーを信じてるので、買うことはないのであった…

COMのアパートメント (4) スレッド親和性

前回までの内容で、COMが裏で何をしているのか、少し見えたような気がすると思う。
ここで重要な「嘘」を公表しなければならない(大げさか)。

今まで、ファサードが存在する理由としてスレッド同期を挙げたのだが、そのこと自体は間違っていないのだが、同期の対象が違っていた。
コンポーネントの「単一のインスタンス」について同期を行うために、ファサードを挿入すべきか否かという話だったのだが、実はそうではない。

本当のCOMでは、同期の対象は「ウインドウ」である(ほぼ)。
Win32 APIの生の呼び出しで、一からウインドウを生成するコードを書いたことがあれば、これは自明である。ウインドウにまつわるAPIの大半は、対象とするスレッドを限定する。

たとえば、Visual C++で、新しくWin32のプロジェクトを開始してみてほしい。ひな形として生成されるCのコード(微妙にC++だが)は、Win32 APIの直接呼出しの、懐かしい、目を背けたくなるコードだ。そのコードには、CreateThreadや、beginthreadexのような、スレッド生成のAPI呼び出しは存在しない。つまり、このコードは「シングルスレッド」で実現されている。

実行すると、単一の味気ないウインドウが表示される。一通りの操作が可能である。ウインドウが生成されると、「ウインドウハンドル」が返される。ウインドウハンドルの値は不透明値なのだが、user32ライブラリ内で非公開の構造体を指しており、この構造体はウインドウを生成したスレッドと結びついている(スレッドハンドルを保持しているのか、スレッドローカルストレージに関連付いているのか、詳細はわからない)。

そして、ウインドウメッセージは、例のメッセージ転送ループ(GetMessage→DispatchMessage)によって、これらのウインドウに転送されるのだが、このループはウインドウが関連付けられているスレッドで実行される(というよりも実行するようにコードを書く)。
プロジェクトテンプレートでは単一のメインスレッドを使うので、これらが全て同じスレッドで実行されるわけだ。

さて、たとえばウインドウにボタンがあったとして、ボタンクリックに反応するコードを書いたとしよう。「シングルスレッド」なので、ボタンクリックの処理中に別のメッセージが発生することはない。
DispatchMessageからウインドウプロシージャが呼び出され、ボタンクリックのお手製コードが走っている。そのため、この処理が終わって、次にGetMessageが呼び出されるまでは、新しいメッセージが目に見える形では現れない。

したがって、マルチスレッド実行による、複数のメッセージの同時発生と実行を考慮する必要はないわけだ。
これは鶏と卵の関係だが、Win32のウインドウメッセージ処理にこういう制限があるから、競合処理は発生しないとも言えるし、競合処理が発生しないようにWin32 APIが設計されているともいえる。

この制限が時には苦痛となる。時間がかかる処理を行う場合だ。そのような処理をボタンクリックの処理で実装すると、ウインドウは他の処理が実行出来ない。ウインドウのサイズ変更・移動・ウインドウの描画まで含めて、「固まった」かのようになる。
仕方がないので、ここでワーカースレッドを生成して、長く時間のかかる処理を委譲したとする。これで、長い処理を並行して実行できるので、ウインドウの基本的な動作に支障は出なくなる(ボタンが2回クリックされないように無効化するなどの工夫は必要だが)。

しかし、このワーカースレッドの処理が完了したときに、それをどのようにウインドウに通知するかと考えた時に問題が発生する。
大半のウインドウAPIが、特定のスレッド(この場合はウインドウを生成したのがメインスレッドなので、メインスレッド)から呼び出されないと機能しない、あるいは誤動作を起こす。そこで、PostThreadMessageなどを使って、メッセージキューに結果となるメッセージを送信する。

メッセージキューはそのうちメインスレッドのGetMessageによって引き出され、晴れて「メインスレッド」でメッセージが処理される。この特別なメッセージの処理コードを書いておけば、ワーカースレッドの処理結果は間接的にメインスレッドの実行中に処理され、ウインドウに何らかの表示を行ったりすることが出来るようになる。

.NET Frameworkにおいても、System.Windows.Forms.Control.Invoke()などを使って、背景では同じことを行う。

乱暴な言い方をすれば、クリティカルセクションがウインドウに紐付いていて、ウインドウを基準に同期を行っていることに等しい。
分かっている人には当たり前のことではあるが、この制限をスレッド親和性のモデルとしたのが、COMのアパートメントというスレッド属性だ。何故か? それはCOMの構造を設計したときに、それがスレッディングにまつわるWindows独自の問題だったからだ(多分)。

何らかのアプリケーションのソースコードが手元にあったとする。このソースコードはすでに規模が大きく、マルチスレッドに対してどこまで安全であるかが分からない。注意深く設計されていれば、スレッドの導入にも頑健であるだろうが、今までにファサード導入で見てきたように、スレッド親和性を担保するには相応のコストが必要だ。おまけに、ウインドウの制御に関しても、どのウインドウがどのスレッドに紐付いていて、どう安全でどう危険であるかも明らかではない。アプリケーションによっては、GetMessageとDispatchMessageによるメッセージループが「複数」の別々のスレッドで実行されている可能性すらある。

また、同じ問題が(一般的なCOMではない)コンポーネントライブラリにも存在する。このような状況下では、あるコンポーネントがあるアプリケーションやコンポーネントと協調して動作させるなど、効率以前の問題で、悪夢としか言いようがない。サードパーティ製のライブラリを結合するとき、スレッド親和性についてほとんどの場合は無視(つまり親和性などなく、ウインドウとスレッドの安全は自分で担保が必要と仮定)するはずだ。

幸い、COMは「インターフェイス境界」が非常にはっきりしている。そのため、この境界を基準として、ウインドウに紐付いた暗黙のスレッドとの競合についても、背後で面倒を見ようとしたわけだ。

つづく


最新のCOM記事:

COMについて、ローカルディスカッションで大幅に拡充してまとめ上げた記事があるので、そちらもどうぞ:「ChalkTalk CLR – COMのすべて」

COMのアパートメント (3) スレッドの属性とコンポーネント

さてと、アパートメントという用語がどのあたりから来るのか匂わせておいたところで、2つの軸をどのように対処するのかを見ていく。

一つ目は、ファサードの有無の選択だ。
main関数で例を示したように、このままではクラスの呼び分けをする必要がある。
そこで、newする実装を隠ぺいすることを考える。たとえば、以下のような関数を用意する。

IAdd *CreateInstance(const bool threadSafe)
{
 IAdd *pInstance = new AddCalculator();

 // スレッドセーフなコンポーネントが必要なら
 if (threadSafe == true)
 {
  return new AddCalculatorFacade(pInstance);
 }
 else
 {
  return pInstance;
 }
}

呼び出す側は、スレッドセーフな実装が必要であればtrueを、そうでなければfalseを与えれば良い。しかし、これではクラスの型を指定するのが、boolの値にすり替わっただけだ。
そこで、いよいよコンポーネント自身にスレッド親和性の情報を付加する時がやってきた。

まず、コンポーネントの実装に、コンポーネント自身のスレッドセーフ属性を加える。これはコンポーネントのインスタンスが生成される前に参照する必要があるので、static関数とする。

class AddCalculator : public IAdd
 {
 public:
 // 途中省略
 static bool IsThreadSafe() throw()
 {
  // このコンポーネントはスレッドセーフではない
  return false;
 }
 };

class AddCalculator2 : public IAdd
 {
 public:
 // 途中省略
 static bool IsThreadSafe() throw()
 {
  // このコンポーネントはスレッドセーフだ
  return true;
 }
 };

IAdd *CreateInstance()
{
 // コンポーネントのスレッドセーフ属性を得る
 const bool componentIsThreadSafe = AddCalculator::IsThreadSafe();
 // const bool componentIsThreadSafe = AddCalculator2::IsThreadSafe();
 IAdd *pInstance = new AddCalculator();

 // コンポーネントがスレッドセーフでなければ
 if (componentIsThreadSafe == false)
 {
  // ファサードを挟んでスレッドセーフにする
  return new AddCalculatorFacade(pInstance);
 }
 else
 {
  return pInstance;
 }
}

まだ納得いかないだろう。特にスレッドセーフ属性を取得するくだりは、コンポーネントの型が分かっているのだから、意味がないように見えるかもしれない。今はテンプレートを使ってお茶を濁しておく。テンプレート経由だが、コンポーネントが自身でスレッドセーフ属性を提供している事がわかると思う。

template <typename T>  IAdd *CreateInstance()
{
 // コンポーネントのスレッドセーフ属性を得る
 const bool componentIsThreadSafe = T::IsThreadSafe();
 IAdd *pInstance = new T();

 // コンポーネントがスレッドセーフでなければ
 if (componentIsThreadSafe == false)
 {
  // ファサードを挟んでスレッドセーフにする
  return new AddCalculatorFacade(pInstance);
 }
 else
 {
  return pInstance;
 }
}

int main()
{
 // まだ実質的な変化はない...
 IAdd *pInstance = CreateInstance<AddCalculator>();
 // IAdd *pInstance = CreateInstance<AddCalculator2>();

 // 使う...
 delete pInstance;
 return 0;
}

上のコードに意味が見いだせないのは当然で、今までやって来た事の形を変えただけだからだ。「ついに」、もう半分の肝心な部分を付け足す。それは、現在のスレッドの指標を加味することだ。

// スレッドローカルストレージ(スレッド毎に保存される変数)
static __declspec(thread) bool g_MultiThreadBound = false;

// 現在のスレッドにスレッド属性を設定する
void SetThreadBound(const bool isMultiThreaded) throw()
{
 g_MultiThreadBound = isMultiThreaded;
}

template <typename T>  IAdd *CreateInstance()
{
 // コンポーネントのスレッドセーフ属性を得る
 const bool componentIsThreadSafe = T::IsThreadSafe();
 IAdd *pInstance = new T();

 // 現在のスレッドがマルチスレッドで使用する前提でかつ、
 // コンポーネントがスレッドセーフでなければ
 if ((g_MultiThreadBound == true) && (componentIsThreadSafe == false))
 {
  // ファサードを挟んでスレッドセーフにする
  return new AddCalculatorFacade(pInstance);
 }
 // コンポーネントがスレッドセーフなら、現在のスレッドがどのような属性でも問題ない。
 // コンポーネントがスレッドセーフでなくても、現在のスレッドがシングルスレッドでのみ使用する属性なら問題ない。
 else
 {
  return pInstance;
 }
}

さて、これで、コンポーネントを使用する側にとっては、スレッドの状態を表明さえすれば、そのコンポーネントがスレッドセーフだろうが、そうでなかろうが、安全に使用できるようになった。使用者はコンポーネントがスレッドに対して安全であるかどうかを考えなくてもよくなったという事だ。
シングルスレッドで使用するなら、

int main()
{
 // シングルスレッドでのみ使用する場合
 SetThreadBound(false);

 // どちらの実装を使ったとしても、最適なインスタンスが提供される。
 IAdd *pInstance = CreateInstance<AddCalculator>();
 // IAdd *pInstance = CreateInstance<AddCalculator2>();

 // 使う...
 delete pInstance;
 return 0;
}

マルチスレッドで使用するなら、

// スレッドのエントリポイント
int ThreadEntryPoint()
{
 // マルチスレッドで使用する場合
 SetThreadBound(true);

 // どちらの実装を使ったとしても、最適なインスタンスが提供される。
 IAdd *pInstance = CreateInstance<AddCalculator>();
 // IAdd *pInstance = CreateInstance<AddCalculator2>();

 // 使う...
 // (場合によっては別のスレッドにポインタを渡して使うかも)
 delete pInstance;
 return 0;
}

なんとなく、普段COMでやっていることに近づいてきたのが分かるだろうか。
つづく。


最新のCOM記事:

COMについて、ローカルディスカッションで大幅に拡充してまとめ上げた記事があるので、そちらもどうぞ:「ChalkTalk CLR – COMのすべて」