Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程:等待窗口打开以执行操作_C#_Multithreading - Fatal编程技术网

C# 线程:等待窗口打开以执行操作

C# 线程:等待窗口打开以执行操作,c#,multithreading,C#,Multithreading,我已经为我的程序编写了一个窗口管理器,它在程序的整个生命周期中(在后台线程上)保持某些窗口的打开状态(如果用户希望打开它们)。 我刚刚为联系人窗口实现了一个操作。问题是,当窗口已打开时,该操作会起作用,但如果在窗口尚未打开时调用该操作,则窗口将打开,但该操作未执行(再次按下按钮将执行该操作) 守则: private static SetupContacts _contactsWindow; private static Thread _contactthread; public static

我已经为我的程序编写了一个窗口管理器,它在程序的整个生命周期中(在后台线程上)保持某些窗口的打开状态(如果用户希望打开它们)。 我刚刚为联系人窗口实现了一个操作。问题是,当窗口已打开时,该操作会起作用,但如果在窗口尚未打开时调用该操作,则窗口将打开,但该操作未执行(再次按下按钮将执行该操作)

守则:

private static SetupContacts _contactsWindow;
private static Thread _contactthread;

public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
    {
        if (_contactsWindow == null)
            CreateContactThread(uo, contactID);

        // make sure it is still alive
        if (!_contactthread.IsAlive)
            CreateContactThread(uo, contactID);

        if (_contactsWindow != null)
        {
            _contactsWindow.BringToFront();
            _contactsWindow.Focus();
            switch (action)
            {
                case ContactFormAction.ViewContact:
                    if (contactID > 0)
                        _contactsWindow.LoadCustomer(contactID); // load the contact
                    break;

                case ContactFormAction.AddNewContact:
                    _contactsWindow.AddCustomer();
                    break;
            }
        }
    }

    private static void CreateContactThread(repUserObject uo, int contactID)
    {
        if (_contactthread == null || !_contactthread.IsAlive)
        {
            _contactthread = new Thread(delegate()
            {
                _contactsWindow = new SetupContacts(uo, contactID);
                _contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
                _contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
                {
                    if (CerberusContactHasBeenSaved != null)
                        CerberusContactHasBeenSaved.Raise(sender, args);
                };
                Application.EnableVisualStyles();
                BonusSkins.Register();
                SkinManager.EnableFormSkins();
                UserLookAndFeel.Default.SetSkinStyle("iMaginary");
                Application.Run(_contactsWindow);
            });
            _contactthread.SetApartmentState(ApartmentState.STA);
            _contactthread.Start();
        }
    }
当例程第一次运行(通过调用ShowTime)时会发生什么情况,即它命中第一个if语句并转到CreateContactThread()例程。这就完成了任务,但当它返回时,_contactsWindow仍然为空。下一次调用例程时(即第二次按下按钮进行调用),由于_contactWindow不为空,因此一切正常。
如何让它一次完成所有任务?

我与评论员Blorgbeard意见一致,他建议运行多个UI线程是个坏主意。当在单个线程中使用API时,API本身工作得最好,并且在单个线程中最容易处理在代码中对UI对象执行的许多类型的操作和操作,因为这样做本质上确保了事情按照预期的顺序发生(例如,变量在使用前已初始化)

也就是说,如果出于某种原因,您确实必须在不同的线程中运行新窗口,那么您可以同步这两个线程,以便初始线程无法继续,直到新线程达到足够的程度,使您希望在新初始化的对象上执行的操作有合理的成功机会(当然,包括最初创建的对象)

有很多技术可以同步线程,但我更喜欢新的
TaskCompletionSource
对象。它使用起来很简单,如果您将代码更新为使用
async
/
wait
,它将很容易与之匹配

例如:

public static void ShowContact(repUserObject uo, ContactFormAction action, int contactID)
{
    CreateContactThread(uo, contactID);

    if (_contactsWindow != null)
    {
        _contactsWindow.BringToFront();
        _contactsWindow.Focus();
        switch (action)
        {
            case ContactFormAction.ViewContact:
                if (contactID > 0)
                    _contactsWindow.LoadCustomer(contactID); // load the contact
                break;

            case ContactFormAction.AddNewContact:
                _contactsWindow.AddCustomer();
                break;
        }
    }
}

private static void CreateContactThread(repUserObject uo, int contactID)
{
    if (_contactthread == null || !_contactthread.IsAlive)
    {
        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

        _contactthread = new Thread(delegate()
        {
            _contactsWindow = new SetupContacts(uo, contactID);
            _contactsWindow.CerberusContactScreenClosed += delegate { _contactsWindow = null; };
            _contactsWindow.CerberusContactHasBeenSaved += delegate(object sender, ContactBeenSavedEventArgs args)
            {
                if (CerberusContactHasBeenSaved != null)
                    CerberusContactHasBeenSaved.Raise(sender, args);
            };

            _contactsWindow.Loaded += (sender, e) =>
            {
                tcs.SetResult(true);
            };

            Application.EnableVisualStyles();
            BonusSkins.Register();
            SkinManager.EnableFormSkins();
            UserLookAndFeel.Default.SetSkinStyle("iMaginary");
            Application.Run(_contactsWindow);
        });
        _contactthread.SetApartmentState(ApartmentState.STA);
        _contactthread.Start();

        tcs.Task.Wait();
    }
}
public static void ShowContact(RepuseObject uo、ContactFormAction动作、int contactID)
{
CreateContactThread(uo,contactID);
如果(_contactsWindow!=null)
{
_contactsWindow.BringToFront();
_contactsWindow.Focus();
开关(动作)
{
案例联系人FormAction.ViewContact:
如果(联系人ID>0)
_contactsWindow.LoadCustomer(contactID);//加载联系人
打破
案例ContactFormAction.AddNewContact:
_contactsWindow.AddCustomer();
打破
}
}
}
私有静态void CreateContactThread(RepuseObject uo,int contactID)
{
如果(_contactthread==null | |!_contactthread.IsAlive)
{
TaskCompletionSource tcs=新的TaskCompletionSource();
_contactthread=新线程(委托()
{
_contactsWindow=新设置的联系人(uo,联系人ID);
_contactsWindow.CerberusContactScreenClosed+=委托{u contactsWindow=null;};
_contactsWindow.CerberusContactHasBeenSaved+=委托(对象发送者,ContactBeenSavedEventArgs参数)
{
如果(CerberusContactHasBeenSaved!=null)
CerberusContactHasBeenSaved.Raise(发送方,args);
};
_contactsWindow.Loaded+=(发件人,e)=>
{
tcs.SetResult(真);
};
Application.EnableVisualStyles();
寄存器();
SkinManager.EnableFormSkins();
UserLookAndFeel.Default.SetSkinStyle(“虚构”);
应用程序运行(_contactsWindow);
});
_SetApartmentState(ApartmentState.STA);
_contactthread.Start();
Task.Wait();
}
}
注意事项:

  • 在我看来,您的代码中存在冗余检查。
    CreateContactThread()
    方法本身检查
    null
    !IsAlive
    ,如果其中任何一个为false,则重新启动线程。因此,理论上,在该方法返回时,调用方应确保所有内容都已按需初始化。并且您只需调用该方法一次。因此,我将代码更改为:只调用该方法一次,并且无条件地这样做(因为如果没有什么要做的,该方法将什么也不做)
  • 调用线程将在
    CreateContactThread()中等待
    方法启动新线程后,直到引发新窗口的
    加载事件为止。当然,窗口对象本身的创建时间早于此,您实际上可以在那时释放调用线程。但在我看来,您可能希望在开始尝试之前完全初始化窗口对象对它做点什么。所以我把同步延迟到了那个点
  • 正如Blorgbeard所指出的,在多个线程中运行UI对象的风险之一是,如果不获得
    InvalidOperationException
    s,就很难访问这些对象。即使它有效,您也不应该真正访问创建它的线程之外的
    \u contactsWindow
    ,但上面的代码就是这样做的(即从原始线程调用
    BringToFront()
    Focus()
    LoadCustomer()
    ,和
    AddCustomer()
    )。我不能保证上面的代码实际上是完全正确的。只是它解决了您所询问的主要同步问题
  • 说到其他可能的bug,您可能有一个未解决的争用条件,即新联系人窗体线程可能正在退出,就像您正在检查它的
    IsAlive
    属性一样。如果您在它退出之前检查该属性,但在它退出后尝试访问该线程和/或窗口,那么您的代码可能会失败