Hatena::Groupsubtech

ういはるかぜの化学

Tuesday, January 02, 2007

Windows Forms とスレッド 05:53  Windows Forms とスレッド - ういはるかぜの化学 を含むブックマーク はてなブックマーク -  Windows Forms とスレッド - ういはるかぜの化学

g:subtech:id:cho45:20070103:1167752564

コメントにしようとしたけど長くなったので。

エラーの内容がわからないので問題とあっているかどうかわかりませんが、Form はスレッドセーフではないため Visual Studio 2005 で作成した Control はメンバの参照時にUIスレッド(Application.Run したスレッド)で操作することを強制してきます。


これを回避するには

という方法があります。

どのスレッドがどうコントロールを利用するのかわかっていて保証できるなら Control.CheckForIllegalCrossThreadCalls プロパティを false にするのが簡単です。

もう一つの推奨解決策である BeginInvoke を使う方法について。C#で書くと

        // Control.CheckForIllegalCrossThreadCalls プロパティが true の前提
        private void button1_Click(object sender, EventArgs e)
        {

            // 別なスレッドを立てて1秒ごとに更新してみる
            (new Thread(delegate(Object p)
            {
                for (Int32 i = 0; i < 10; i++)
                {
                    // スレッドが違うと怒られる
                    label1.Text = i.ToString();
                    // 1秒おきに表示なので待つ
                    Thread.Sleep(1000);
                }
            })).Start();
        }

は怒られて止まってしまいますが、以下のようにすると動きます。

        delegate void ChangeLabelDelegate(Int32 i);
        // 実際の処理を書いたメソッド
        private void ChangeLabel(Int32 i)
        {
            label1.Text = i.ToString();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            // 別なスレッドを立てて1秒ごとに更新してみる
            (new Thread(delegate(Object p)
            {
                for (Int32 i = 0; i < 10; i++)
                {
                    // UI スレッドで実行してもらう
                    IAsyncResult ar = label1.BeginInvoke(new ChangeLabelDelegate(ChangeLabel), i);
                    // 実行完了まで待機(同期処理と同じ)
                    label1.EndInvoke(ar);
                    // 1秒おきに表示なので待つ
                    Thread.Sleep(1000);
                }
            })).Start();
        }

で、BeginInvoke にはDelegateクラスの何かを渡さなければいけないので、Rubyclr でどうすればいいんだろうと思ったんですがよくわかりませんでした。というか試そうと思ったらRuby win32を必要としてビルドとかめんどくさいこと極まりないのであきらめました。

ついでにIronPythonだとこんな感じ。

import clr
from System.Threading import Thread, ThreadStart
from IronPython.Runtime.Calls import CallTarget1
clr.AddReferenceByPartialName("System.Windows.Forms")
from System.Windows.Forms import Application, Form, Button, Label, Control

Control.CheckForIllegalCrossThreadCalls = True; # デフォルト false っぽい

class MainForm(Form):
    def __init__(self):
        self.b = Button()
        self.l = Label()
        self.Load += self.OnLoad

    def DoChange(self, i):
        self.l.Text = i.ToString()

    def RunCount(self):
        for i in range(0, 10):
            ar = self.BeginInvoke(CallTarget1(self.DoChange), i)
            self.EndInvoke(ar)
            Thread.Sleep(1000)
        
    def OnLoad(self, e):
        self.b.Text = "Run"
        self.l.Text = "0"

        self.Controls.Add(self.b)
        self.Controls.Add(self.l)
        self.l.Top = self.b.Bottom + 10
        
        self.b.Click += self.OnClicked

    def OnClicked(self, sender, e):
        Thread(ThreadStart(self.RunCount)).Start()

Application.Run(MainForm())

IronPythonの場合にはIronPython.Runtime.CallsのCallTargetN(Nはパラメータ数)というのを使うとDelegateになります。

まあC#が一番楽というオチが。


FirefoxJavaScript からバイナリを読み取る 18:19 Firefox で JavaScript からバイナリを読み取る - ういはるかぜの化学 を含むブックマーク はてなブックマーク - Firefox で JavaScript からバイナリを読み取る - ういはるかぜの化学

d:id:k12u:20061216:p1 から。

はー。なるほどー。

req.overrideMimeType('text/plain; charset=x-user-defined');

こうすればよかったのですね。以前試してダメでどうしたものか悩んだのですよね。

トラックバック - http://subtech.g.hatena.ne.jp/mayuki/20070102