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