FC2ブログ

GetExportedValueのインスタンス破棄

MEFでプラグインを作る場合、PartCreationPolicy属性でNonSharedを指定すると、Composer.GetExportedValueするたびに新しいインスタンスが作成される。
だがしかし、以前のインスタンスが破棄されない。こいつが困った。

結論から言うと、importするクラスを返すGetExportedValueでは作成されたインスタンスを破棄することはできない。

NonSharedでExportしたインスタンスを破棄するには、Composer.ReleaseExportを使わねばならない。
しかしこのReleaseExportの引数はインスタンス化されたクラスオブジェクトではなく、Lazy exportになるので、Composer.GetExportで取得したexportを保存しておき、不要になった時点でReleaseExportしてやる。

インスタンス化されたオブジェクトは、export.Valueで取得できる。

とまあそんな感じ。
スポンサーサイト

Threadのメッセージポンプ

C#のSystem.Threading.Threadはなかなかいいもんで。
ワーカスレッドならより簡単なのだが、常駐スレッドってのも業務系では良く使う。
で、ワーカスレッドにメッセージポンプを持たせるとするとこんな。
-- ThreadQueue.cs --
using System;
using System.Collections.Generic;
using System.Threading;

namespace MyThread
{
public class ThreadQueue
{
private List<object> queue_;
private ManualResetEvent mrevent_;
public int Count {
get { return queue_.Count; }
}
public ThreadQueue ()
{
queue_ = new List<object>();
mrevent_ = new ManualResetEvent(false);
}
public void Add(object o)
{
lock(this) {
queue_.Add (o);
mrevent_.Set ();
}
}
public object Get()
{
if (queue_.Count == 0) {
mrevent_.WaitOne ();
}
return getMsg ();
}
private object getMsg()
{
object o = null;
lock(this) {
if (queue_.Count > 0) {
o = queue_[0];
queue_.RemoveAt (0);
mrevent_.Reset ();
}
}
return o;
}
}
}
メッセージが何も無い時は、GetはManualResetEventでブロックして待つことにする。
-- ThreadObject.cs --
using System;
using System.Collections.Generic;
using System.Threading;

namespace MyThread
{
public class ThreadObject
{
private string id_;
private Thread thread_;
private MyThread.ThreadQueue queue_;
public ThreadObject (int n)
{
queue_ = new MyThread.ThreadQueue();
thread_ = new Thread(this.DoWork);
thread_.Start (n.ToString ());
}
public void Send(object o)
{
queue_.Add (o);
}

private void DoWork(object o)
{
id_ = (string)o;
object msg;
while ((msg = queue_.Get()) != null) {
System.Console.WriteLine (id_
+ ":" + ((int)msg).ToString ()
+ " ::(" + queue_.Count.ToString () + ")");
Thread.Sleep (int.Parse (id_) * 200);
}
System.Console.WriteLine (id_ + ":End");
}
}
}
ThreadQueueを抱えてワーカスレッドを起こすThreadObject。
DoWorkの中のwhileがメッセージポンプになる。
キューの溜まり具合を確認するためにSleepを入れてある。
実際にはThreadQueueに格納するのは、業務的なobjectになるだろう。
で、これを動かしてみる。
-- Main.cs --
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace MyThread
{
class MainClass
{
public static void Main (string[] args)
{
Parallel.For (0, 3, i => {
var th = new MyThread.ThreadObject(i);
for (int j = 1; j < 10; j++) {
th.Send (j);
}
th.Send(null);
});
}
}
}

で実行すると、それらしく動く。
-- 実行結果 --
1:1 ::(9)
0:1 ::(9)
2:1 ::(9)
0:2 ::(8)
0:3 ::(7)
0:4 ::(6)
0:5 ::(5)
0:6 ::(4)
0:7 ::(3)
0:8 ::(2)
0:9 ::(1)
0:End
1:2 ::(8)
1:3 ::(7)
2:2 ::(8)
1:4 ::(6)
2:3 ::(7)
1:5 ::(5)
1:6 ::(4)
2:4 ::(6)
1:7 ::(3)
1:8 ::(2)
2:5 ::(5)
1:9 ::(1)
1:End
2:6 ::(4)
2:7 ::(3)
2:8 ::(2)
2:9 ::(1)
2:End

Press any key to continue...

まー、Linuxのmonoでも、Windowsの.NETでもそれらしく動く。
実際の業務ではThreadObjectを管理するクラスで、複数常駐スレッドを動かしちゃう、みたいなことをする。
Cでプロセスたくさん作って、セマフォであれこれ同期したり、共有メモリ使ったりするよりもC#は断然楽ではある。

Parallel

.NET4から追加されている並列処理。
まぁ簡単なものならParallel.For
Parallel.For (0, n, i => {
    //並列処理
});

なんてのが気軽にできちゃって期待度アップアップなんだけれど・・・

これがなかなか実際速くならんのだよね。
”ここは並列処理でしょ!”とか意気込んで書いても、単純ループよりも倍の時間がかかっちゃったりすることがざらにある。
まぁマシンの地力もあるし、有効な環境もいくらでもあるんだろうけれども、業務の陰でこそこそ試している限り速くなった試しがない。うーん。

動作をしっかり把握するなら、きっちりスレッドクラスを作り込んじゃうだろうし、Parallel.Forの出番は”ちゃちゃっと”やっちゃえる手軽さなんだけどな。
どこかでこの"Parallel使い倒しちゃってます"みたいな人居るかなぁ・・・

SortedDictionary

元々Dictionaryを使っていた場面で、Keyでソートしたい要求が出た。
C++のstd::mapならば格納の度にソートしてくれちゃうので有難い事が多いんだが、C#のDictionaryはそんな事はしてくれない。
foreachで取得すると、律儀に格納した順番に渡してくれる。まぁそりゃそうだって話なんだが。

で、Dictionaryをソートしようと思案することしばし・・・
DictionaryにはSortのメソッドが無いのでまずはKeysでSortして、かぁ?、いやそんな手間かけなきゃならないのが何かおかしい。そんなはずはないはず、はず、はず。
んにょんにょしながら10分経過。

あ、SortedDictionaryがあるじゃんかよ、と思い出す。
さくっとDictionaryをSortedDictionaryに書き換えて終了。

んー、ちうか、CだったりC++だったりCLIだったりC#だったりJavaだったりと、作業が短時間で移りすぎなんだよ。言語が変わると頭の切り替えにたっぷり時間かけないと無理なんだからさ。
だがこれは歳のせいじゃなく若い時からそうだったかもしれないと遠い目をしてごまかしてみる。

MonoでC#

LinuxでもMonoが使えるのでC#プログラミングができる。
しかもSystem.Windows.Formsなんかもそのまま使えるので、Windowsで作成したアプリケーションもLinuxのMonoでそのまま動かせる!

ってのが建前なんだけれども、実際のところC#で書かれたWindowsアプリケーションで、[dllimport]つまりはWin32APIを使っていないものなど殆どないので、Linuxでは動かんのです。
逆に言えば、Win32API呼び出ししないように作れば、Linuxであってもそのまま動くと言えるんだが、
SendMessageやFindWindowやらMMFやらを.NETでごにょごにょするのは結構面倒くさい。

しかしこの面倒くさい問題をクリアしさえすれば、LinuxでMonoでC#でサーバシステム構築なんてこともできてしまう。
強力なマネージド言語であるC#であれば、サーバシステムといえど大幅な工数削減が見込めるし、記述が煩雑になり技術者のスキルの問題で敬遠されがちだったマルチスレッドもさくさく使えるようになる。
(Cでのサーバ構築の場合、業務系の多くのシステムでは機能毎にプロセスを分け、プロセス間通信によりシステムとしての動作をさせてきている。これをスレッド操作とすることで結構楽になる事も多い。)

本来営業品目はあくまでC/C++なのでC#が主流になっても困るんだが、LinuxサーバでC#で業務系サーバってのもちょっと面白そうなので、機会があったら試してみようと思う。
プロフィール

f_yamaki

Author:f_yamaki

アクセスカウンタ
最近の記事
最近のコメント
最近のトラックバック
月別アーカイブ
カテゴリー
ブロとも申請フォーム

この人とブロともになる

ブログ内検索
RSSフィード
リンク