C# c中偶尔会出现调用问题#

C# c中偶尔会出现调用问题#,c#,winforms,invoke,C#,Winforms,Invoke,我经常遇到invokerequired方法的问题,如果有人知道这个问题或者可以向我解释的话,我想询问一下。我有一些代码: splashScreen splashObj = splashScreen.GetInstance(); if (thrd == null) { thrd = new Thread(new ThreadStart(loadingScreenStart)); thrd.IsBackground

我经常遇到invokerequired方法的问题,如果有人知道这个问题或者可以向我解释的话,我想询问一下。我有一些代码:

 splashScreen splashObj = splashScreen.GetInstance();

        if (thrd == null)
        {
            thrd = new Thread(new ThreadStart(loadingScreenStart));
            thrd.IsBackground = true;
            thrd.Start();
        }

        else
        {
            if (splashObj.InvokeRequired)
            {
                splashObj.Invoke((MethodInvoker)delegate()
                {
                    splashObj.Show();
                }
                );
            }
            else
            {
                splashObj.Show();
            }
        }

        if (splashObj.loadLabel.InvokeRequired)
        {
            splashObj.loadLabel.Invoke((MethodInvoker)delegate()
            {
                splashObj.loadLabel.Text = "Checking Username...";
            }
            );
        }
        else
        {
            splashObj.loadLabel.Text = "Checking Username...";
        }


  public void loadingScreenStart()
    {
        splashScreen splashObj = splashScreen.GetInstance();
        Application.Run(splashScreen.GetInstance());
    }
splashscreen代码:

 public partial class splashScreen : Form
{

    public Form1 form1;

    public splashScreen()
    {
        InitializeComponent();
        //form1 = frm1;
    }

    public splashScreen(Form1 frm1)
    {
        form1 = frm1;
    }


    private void splashScreen_Load(object sender, EventArgs e)
    {

    }

    private static splashScreen m_instance = null;
    private static object m_instanceLock = new object();

    public static splashScreen GetInstance()
    {

        lock (m_instanceLock)
        {
            if (m_instance == null)
            {
                m_instance = new splashScreen();
            }
        }

        return m_instance;
    }
}
所以我在后台加载其余代码时显示了一个splashscreen。显示的信息有“检查用户名…”、“检查密码…”、“加载…”等等。
这段代码运行得很好,但有时似乎代码执行速度比线程快,然后我得到一个错误,该方法是从创建它的线程以外的其他线程调用的。为什么会发生这种情况?这类问题有什么解决办法吗?这种情况可能在二十次处决中发生一次

多线程很难实现,因此您应该尽可能简化它。首先,没有理由在splashscreen类本身之外有任何splashscreen代码。第二,您应该始终知道自己在做什么,因此,
InvokeRequired
只是一种代码气味,上面写着“我不知道谁将调用此方法”

这是LINQPad的一个代码段,您需要删除
InitializeComponent
上的注释以及
loadLabel
控件(该控件应位于设计器上)

这样,启动屏幕的逻辑就与应用程序的其余部分完全隔离了。要显示splashscreen文本,只需调用
splashscreen.ShowText
。要使splashscreen消失,请调用
splashscreen.Done

请注意,无需使用
invokererequired
——任何合法的
SplashScreen调用都是不可能的。ShowText
(或
Done
)不需要编组到SplashScreen的UI线程

现在,这并不完美。这是我在大约10分钟内写的东西。但它(可能:)是线程安全的,比最初的版本更好地遵循最佳实践,并且更易于使用

此外,在某些情况下,我会使用更高级别的构造,例如
惰性
任务
——但由于这在这里没有真正的帮助(启动新的消息传递循环,在表单关闭时必须重新创建表单……),因此我选择了更简单的解决方案


请注意,我使用的是
Invoke
,而不是
BeginInvoke
或类似的方法,这一点非常重要,因为如果不这样做,就可以对关闭进行排队,然后是一个
ShowText
,这将对已处理的表单起作用。如果您打算从多个线程调用
ShowText
,也可以更安全地锁定整个
ShowText
正文。考虑到闪屏的常见用例,有一些线程安全是不必要的,但是…

好的,所以这个问题仍然存在,当线程在表单显示之前调用表单时,它可能会发生;invokererequired和BeginInvoke都可能失败

解决方案是在表单事件上启动线程,如图所示:

//
///测试表
/// 
公共部分类MyForm:Form
{
/// 
///工作
/// 
私有线程读取;
/// 
///新测试表格
/// 
公共MyForm()
{
//初始化组件
this.InitializeComponent();
}
/// 
///第一次的形式是显示
/// 
显示私有void MyForm_(对象发送方,事件参数e)
{
//创建并启动线程
this.\u MyThread=新线程(this.MyThreadWork);
这个。_MyThread.Start();
}
/// 
///在这里做些工作
/// 
私人阅读工作()
{
//计数时间
整数计数=0;
//永远
while(true)
{
this.PrintInfo((count++.ToString());
睡眠(1000);
}
}
/// 
///将信息打印到表单标题中
/// 
私有void打印信息(字符串信息)
{
//需要同步吗?
if(this.invokererequired)
{
//同步我
this.BeginInvoke(新操作(this.PrintInfo)、info);
}
其他的
{
//打印信息
this.Text=info;
}
}
/// 
///再见表单,不要错过工作
/// 
私有void MyForm_FormClosed(对象发送方,FormClosedEventArgs e)
{
//杀戮工人
这是。_MyThread.Abort();
}
}

有些人只会添加一个线程。在第一次打印信息之前睡眠,但通常情况下,你会使用什么睡眠时间?1, 10, 100? 取决于表单的显示速度。

if(thrd==null)构造相当不寻常。另外,您的代码示例不完整-如何调用它?此外,splashObj.loadLabel部分可能应该移动到第一个else分支。您不能仅仅启动线程,并期望在
start
调用完成时完成。初始化splashscreen表单并调用
应用程序将需要一些时间。运行
——直到所有这些发生,您才能使用表单。此外,显示文本的逻辑应该是splash形式,这是谁负责显示它。然后,它可以简单地确保它已经初始化(例如,它可以等待
Load
事件中
Set
ManualResetEvent
)。这叫做同步,这在共享内存多线程中是绝对必要的。如果您使用的是.NET 3.5或更高版本,而不是
Invoke((MethodInvoker)delegate()
,您可以使用一点lambda并将其更改为
Invoke(新操作()=>
真的,仅仅是你在使用
InvokeRequired
这一事实就说明了你对正在发生的事情的看法是多么模糊。怎么会有不需要的情况呢?使用
InvokeRequired
被认为是一种代码味道,而你的代码在任何方面都不是特别的:)我想它应该是完整的。我错过了什么吗?是否有可能以某种方式将代码的执行保持到第二天
void Main()
{
    SplashScreen.ShowText("Loading 1");
    Thread.Sleep(1000);
    SplashScreen.ShowText("Loading 2");
    Thread.Sleep(2000);
    SplashScreen.Done();
    Thread.Sleep(2000);
    SplashScreen.ShowText("Loading 3");
}

// Define other methods and classes here
public partial class SplashScreen : Form
{
  private static SplashScreen instance;
  private static readonly ManualResetEvent initEvent = new ManualResetEvent(false);

  Label loadLabel;

  private SplashScreen()
  {
    // InitializeComponent();
    loadLabel = new Label();
    Controls.Add(loadLabel);

    Load += (s, e) => initEvent.Set();
    Closing += (s, e) => initEvent.Reset();
  }

  private static object syncObject = new object();
  private static void InitializeIfRequired()
  {
    // If not set, we'll have to init the message loop
    if (!initEvent.WaitOne(0))
    {
      lock (syncObject)
      {
        // Someone initialized it before us
        if (initEvent.WaitOne(0)) return;

        // Recreate the form if it was closed
        instance = new SplashScreen();

        var thread = new Thread(() => { Application.Run(instance); });
        thread.Start();

        // Wait until the form is ready
        initEvent.WaitOne();
      }
    }
  }

  public static void ShowText(string text)
  {
    InitializeIfRequired();

    instance.Invoke((Action)(() => 
     { 
       if (!instance.IsDisposed) instance.loadLabel.Text = text; 
     }
    ));
  }

  public static void Done()
  {
    // Is it closed already?
    if (!initEvent.WaitOne(0)) return;

    lock (syncObject)
    {
      // Someone closed it before us
      if (!initEvent.WaitOne(0)) return;

      instance.Invoke((Action)(() => { instance.Close(); }));
    }
  }
}
/// <summary>
/// Testing form
/// </summary>
public partial class MyForm : Form
{
    /// <summary>
    /// Work work
    /// </summary>
    private Thread _MyThread;

    /// <summary>
    /// New test form
    /// </summary>
    public MyForm()
    {
        // Initialize components
        this.InitializeComponent();
    }

    /// <summary>
    /// First time form is show
    /// </summary>
    private void MyForm_Shown(object sender, EventArgs e)
    {
        // Create and start thread
        this._MyThread = new Thread(this.MyThreadWork);
        this._MyThread.Start();
    }

    /// <summary>
    /// Doing some work here
    /// </summary>
    private void MyThreadWork()
    {
        // Counting time
        int count = 0;

        // Forever and ever
        while (true)
        {
            this.PrintInfo((count++).ToString());
            Thread.Sleep(1000);
        }
    }

    /// <summary>
    /// Printing info into form title
    /// </summary>
    private void PrintInfo(string info)
    {
        // Needs to sync?
        if (this.InvokeRequired)
        {
            // Sync me
            this.BeginInvoke(new Action<string>(this.PrintInfo), info);
        }
        else
        {
            // Prints info
            this.Text = info;
        }
    }

    /// <summary>
    /// Good bye form, don't miss the work work
    /// </summary>
    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Kill worker
        this._MyThread.Abort();
    }
}