AppDomainをスレッドごとに分ける
PC上で使うことだけを想定されて作成された、static変数にデータを入れるようなライブラリをWebサービスで使わなければならない。簡単に言うとこんなライブラリ。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyLib { public class MyClass { private static string _message; public string Message { get { return _message; } set { _message = value; } } public string greeting(string name) { return _message + ", " + name; } } }
これをマルチスレッド環境で正しく動作させるため、スレッドごとにAppDomainを分けてみる。
using System; using System.ComponentModel; using System.Reflection; using System.Threading; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using MyLib; namespace MyService { // Webサービスメソッド [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ToolboxItem(false)] public class MyService : WebService { [WebMethod] public string MyMethod(string message, string name) { // このインスタンスとそのクラスは、全てスレッドローカル // static変数もスレッドごとに異なる MyClass instance = ThreadLocalMyClass.Instance; // class を使った処理… clazz.Message = message; return instance.greeting(name); } } internal class ThreadLocalMyClass { [ThreadStatic] private static AppDomain _domain; [ThreadStatic] private static MyClass _instance; private static AppDomain Domain { get { if (_domain == null) { _domain = AppDomain.CreateDomain( String.Format("AppDomain{0}", Thread.CurrentThread.GetHashCode()), null, AppDomain.CurrentDomain.SetupInformation); } return _domain; } } public static MyClass Instance { get { if (_instance == null) { // 引数はnamespace、クラス完全修飾名 _instance = (MyClass)Domain.CreateInstanceAndUnwrap( "MyLib", "MyLib.MyClass"); } return _instance; } } } }
こんな感じで良いのかねぇ。
ThreadStaticって楽ですね。