AppDomain,Serialization和System.Threading.Timer问题

时间:2011-11-15 17:50:56

标签: c# serialization .net-4.0 timer appdomain

我有一个使用CreateInstanceAndUnwrap在AppDomain中创建的类。该类包含System.Threading.Timer。

我遇到的问题是,当实例化类时,计时器的回调方法似乎看不到类实例的正确值。

我在下面有示例代码说明问题:

图书馆类

using System;
using System.Threading;

namespace Library
{
    [Serializable]
    public class Class1
    {
        public Class1()
        {
            Started = false;

            _Timer = new Timer(TimerMethod);
        }

        public bool Started { get; set; }

        private readonly Timer _Timer;
        private string _Message;
        private string _TimerMessage;

        public bool Start()
        {
            Started = true;

            _Message = string.Format("Class1 says Started = {0}", Started);
            _TimerMessage = "Timer message not set yet";

            _Timer.Change(1000, 1000);

            return Started;
        }

        public string GetMessage()
        {
            // _TimerMessage is never set by TimerMethod when this class is created within an AppDomain
            return string.Format("{0}, {1}", _Message, _TimerMessage);
        }

        public void TimerMethod(object state)
        {
            // Started is always false here when this class is created within an AppDomain
            _TimerMessage = string.Format("Timer says Started = {0} at {1}", Started, DateTime.Now);
        }
    }
}

消费者类

using System;
using System.Windows.Forms;
using Library;

namespace GUI
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            var appDomainSetup = new AppDomainSetup
            {
                ApplicationName = "GUI",
                ApplicationBase = AppDomain.CurrentDomain.BaseDirectory
            };

            _AppDomain = AppDomain.CreateDomain(appDomainSetup.ApplicationName,
                                                AppDomain.CurrentDomain.Evidence,
                                                appDomainSetup);

            _Class1 = _AppDomain.CreateInstanceAndUnwrap("Library", "Library.Class1") as Class1;
        }

        private readonly AppDomain _AppDomain;
        private readonly Class1 _Class1;

        private void button1_Click(object sender, EventArgs e)
        {
            _Class1.Start();
            MessageBox.Show(_Class1.GetMessage());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(_Class1.GetMessage());
        }
    }
}

运行上面的代码时,GetMessage()始终返回:

Class1表示Started = True,Timer消息尚未设置

但是,如果我更改为上面表单的构造函数来创建Class1的本地实例,

        public Form1()
        {
            InitializeComponent();

            _Class1 = new Class1();
        }

GetMessage()返回预期的消息:

Class1表示Started = True,Timer在11/15/2011 12:34:06 PM表示Started = True

我搜索了Google,MSDN和SO,但未找到任何专门针对AppDomain,Serialization和System.Threading.Timer组合的信息。我也无法找到有关TimerCallback无法引用实例化Timer的本地成员的任何信息。

2 个答案:

答案 0 :(得分:3)

大多数情况下,它是由“按价值编组”(您的班级)和“按参考编组”(很可能是您想要的)之间的差异造成的。如果类不是从MarshalByRefObject派生而是在远程处理时表现得像值类型,则意味着您可以在通信的每一侧获得对象的复制。如果type派生自MarshalByRefObject而不是在没有实例化该对象的一方获得代理,那么该方将能够在另一个AppDomain中的实例上调用方法。

链接:

MarshalByRefObject - http://msdn.microsoft.com/en-us/library/system.marshalbyrefobject.aspx

MSDN杂志中的跨应用程序域调用期间的终身管理 - 下载2003年12月号MSDN magazine(您可能需要取消阻止文件属性中的内容)或使用Web存档链接Managing the Lifetime of Remote .NET Objects with Leasing and Sponsorship

答案 1 :(得分:0)

TimerMethod中的评论说:

// Started is always false here when this class is created within an AppDomain

但是你的输出表明Started是真的。

这是什么?

实际上,当你创建一个本地实例时,我很惊讶它是有效的。 Class1构造函数创建计时器并为其提供回调,但它不设置间隔或到期时间,这意味着计时器不会触发。

当您致电Start时,计时器已初始化,但是给定时间为1秒。 Start返回,您致电GetMessage以获取消息。但是如果你在计时器有机会执行其回调之前调用GetMessage,你将会得到你描述的行为。

如果你在拨打Start和拨打GetMessage之间有1秒的延迟,我想你会发现问题是......时间:你正试图让在计时器有机会设置它之前的消息。请尝试以下方法进行验证:

private void button1_Click(object sender, EventArgs e)
{
    _Class1.Start();
    Thread.Sleep(1000);
    MessageBox.Show(_Class1.GetMessage());
}

或者,我想你可以在几秒钟的延迟后再次点击按钮。