CLR上ではC#、VB.NET等が動作しますが、JScript.NETは言語がJavaScriptという事もあって実はC#やVB.NET等と同等に使えない機能がいくらかあります。
JavaScriptは動的言語です。オブジェクトにプロパティを生やしてメソッドにしたり、prototypeを弄ったり、eval()で関数を生成したりも出来ます。つまりコンパイルするとは言えど、型が動的に変えられる言語です。
その為、.NET Frameworkの機能の一つであるDelegateが使えません。.NET FrameworkのThreadはDelegateという関数型拘束によりスレッドを安全に呼び出せる様になっています。この関数型拘束に緩い拘束なJavaScript(JScript)の関数を渡す事は出来ないのです。
MSDNでもJScriptのDelegate利用制限が書かれています。
ThreadStart デリゲートDelegate.CreateDelegate()というメソッドも存在しますが、こちらもJScriptからは利用出来ません。[JScript] JScript では、.NET Framework のデリゲートを利用することができます。ただし、独自に定義することはできません。
http://msdn.microsoft.com/ja-jp/library/system.threading.threadstart(VS.71).aspx
じゃぁDelegateを引数に取るThreadはJScriptから作れないじゃないか!そう思っておられる方が殆どだと思います。
いえ、出来ます。
import System
import System.Reflection
import System.CodeDom.Compiler
import System.Threading
import Microsoft.CSharp
/**
* createThread()
* @param f thread function
* @param a argument passing to function
* @return thread object
*/
var createThread = (function() {
var source = [
"using System.Threading;",
"public class ThreadInvoker {",
" private Microsoft.JScript.Closure func;",
" private object arg = null;",
" public ThreadInvoker(Microsoft.JScript.Closure func, object arg) { this.func = func; this.arg = arg; }",
" private void invokerThread() { func.Invoke(null, new object[]{this.arg}); }",
" public Thread createInvoker() { return new Thread(invokerThread); }",
"}",
].join("\n");
var cp = CodeDomProvider.CreateProvider("CSharp");
var cps = new CompilerParameters();
cps.ReferencedAssemblies.Add("Microsoft.JScript.dll");
cps.GenerateInMemory = true;
var cr = cp.CompileAssemblyFromSource(cps, source);
var asm = cr.CompiledAssembly;
return function(f, a) {
var tt : Type = asm.GetType("ThreadInvoker");
var args = new System.Object[2];
args[0] = f;
args[1] = a;
return tt.InvokeMember(
"createInvoker",
BindingFlags.InvokeMethod,
null,
Activator.CreateInstance(tt, args),
null);
};
})();
C#上で書かれたスレッドインボーカをメモリ内でコンパイルして、それにJScriptの関数、正しくはクロージャを渡します。ここから先はCLRなので、どうとでも出来るはずでC#からMicrosoft.JScript.Closureのメソッドを叩く事も出来ます。
これを使えば
var th = createThread(function(v) {
for (var n = 1; n <= 6; n++) {
Thread.Sleep(200);
print("thread" + n + " " + v);
}
}, "foo");
th.Start();
Thread.Sleep(200);
print("main1");
th.Suspend();
Thread.Sleep(200);
print("main2");
Thread.Sleep(200);
print("main3");
th.Resume();
こんなコードで
thread1 foo
main1
main2
main3
thread2 foo
thread3 foo
thread4 foo
thread5 foo
thread6 foo
こんな動作をさせる事も出来ます。JavaScript(JScript)でスレッドが使えると色々とやりたくなってきます。JScriptでサーバアプリなんて事も簡単に出来そうですね。ちなみに、このcreateThread()を使うと、ウェブサーバなんかも書けたりします。試しに書いてみたらコードの少なさ(createThreadは除いて)に少し驚きました。JScript.NET & C# ばんざい!
import System
import System.Reflection
import System.CodeDom.Compiler
import System.Threading
import Microsoft.CSharp
/**
* createThread()
* @param f thread function
* @param a argument passing to function
* @return thread object
*/
var createThread = (function() {
var source = [
"using System.Threading;",
"public class ThreadInvoker {",
" private Microsoft.JScript.Closure func;",
" private object arg = null;",
" public ThreadInvoker(Microsoft.JScript.Closure func, object arg) { this.func = func; this.arg = arg; }",
" private void invokerThread() { func.Invoke(null, new object[]{this.arg}); }",
" public Thread createInvoker() { return new Thread(invokerThread); }",
"}",
].join("\n");
var cp = CodeDomProvider.CreateProvider("CSharp");
var cps = new CompilerParameters();
cps.ReferencedAssemblies.Add("Microsoft.JScript.dll");
cps.GenerateInMemory = true;
var cr = cp.CompileAssemblyFromSource(cps, source);
var asm = cr.CompiledAssembly;
return function(f, a) {
var tt : Type = asm.GetType("ThreadInvoker");
var args = new System.Object[2];
args[0] = f;
args[1] = a;
return tt.InvokeMember(
"createInvoker",
BindingFlags.InvokeMethod,
null,
Activator.CreateInstance(tt, args),
null);
};
})();
import System.Net
import System.Net.Sockets
var listener = new System.Net.Sockets.TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
listener.Start();
while (true) {
createThread(function(client) {
var stream = client.GetStream();
var buf = new System.Byte[client.ReceiveBufferSize];
var bytesRead = stream.Read(buf, 0, buf.length);
var str = [
"HTTP/1.0 200 OK",
"Content-Type: text/html",
"",
"<html><body>",
"<b>hello world</b>: " + (new Date).toString(),
"</body></html>",
].join("\r\n");
var bytes = System.Text.Encoding.Default.GetBytes(str);
stream.Write(bytes, 0, bytes.length);
stream.Close();
client.Close();
}, listener.AcceptTcpClient()).Start();
}
マルチスレッドウェブサーバです。カッコイー!