[英]Cross-thread operation not valid. Multiple solutions fail to work,
在阅读之前,我希望每个阅读此书的人都知道我已经尝试了从整个堆栈溢出中使用多个委托/跨线程/调用解决方案。
这么说,这就是我的程序应该做的:
Worker Thread 1 is called to start Async Operation.
If it detects a line that has a typical PRIVMSG header along with the word subscribed!
Create a new MetroTaskWindow with a TaskWindowControl and Add it to the queue
Worker Thread 2 is called after worker thread 1
Worker Thread 2 checks every 5 seconds if queue contains something
If it does, show it and get rid of it
这是相关的代码, 如果您需要更多,请告诉我上述要求:
工作线程1 段
string line = ""; while (!backgroundWorker1.CancellationPending) { try { line = reader.ReadLine(); } catch { } if (line != null && !line.Contains("JOIN")) { try { if (line.Contains("PING") && !line.Contains("PRIVMSG")) { writer.Write(line.Replace("PING", "PONG")); Trace.WriteLine(line.Replace("PING", "PONG")); } else if (line.Split(new char[] { ' ' })[0].Equals(":twitchnotify!twitchnotify@twitchnotify.tmi.twitch.tv") || line.Split(new char[] { ' ' })[0].Equals(":stds_catchemall!stds_catchemall@stds_catchemall.tmi.twitch.tv") && line.Contains("subscribed!")) { total += 1; checkNotifications(); } } catch { continue; } } if (!String.IsNullOrEmpty(line)) Trace.WriteLine(line); } } private void checkNotifications() { List<Achievement> tempQueue = new List<Achievement>(); foreach (Achievement a in achievements) { //I know i could shorten this, but i need it left like this... if (a.AfterSub) tempQueue.Add(a); if (total - a.Goal == start) tempQueue.Add(a); if (total == a.Goal) tempQueue.Add(a); } foreach (Achievement a in Sort(tempQueue)) { MetroTaskWindow m = new MetroTaskWindow(a, this, a.Type.ToString(), new TaskWindowControl(a.Name, a.Message, a), 4, r, ((ScreenRegion)r).getGS()); queue.Add(m); } }
工作线程2
private void CheckAvailable_DoWork(object sender, DoWorkEventArgs e) { while (true) { Thread.Sleep(5250); BeginInvoke((MethodInvoker)delegate { if (queue.Count > 0) { queue[0].Show(); // <---- Error Occurs Here //Cross-thread operation not valid: Control 'TaskWindowControl' accessed from a thread other than the thread it was created on. queue.RemoveAt(0); } }); } }
地铁任务窗口
public MetroTaskWindow(Achievement a, IWin32Window parent, string title, Control userControl, int secToClose, MetroForm r, Form gs) { controlContainer = new MetroPanel(); Controls.Add(controlContainer); controlContainer.Controls.Add(userControl); userControl.Dock = DockStyle.Fill; closeTime = secToClose * 500; this.a = a; form = r; chroma = gs; p = (Form1)parent; this.Text = title; this.Resizable = false; this.Movable = true; this.StartPosition = FormStartPosition.Manual; if (parent != null && parent is IMetroForm) { this.Theme = ((IMetroForm)parent).Theme; this.Style = ((IMetroForm)parent).Style; this.StyleManager = ((IMetroForm)parent).StyleManager.Clone(this) as MetroStyleManager; this.ShadowType = MetroFormShadowType.None; } switch (a.Type) { case PopupType.Achievement: Text = "Achievement!"; break; case PopupType.Milestone: Text = "Milestone!"; break; case PopupType.Notification: Text = "Notification"; break; } }
TaskWindowControl
public partial class TaskWindowControl : UserControl { public TaskWindowControl(string name, string info, Achievement a) { InitializeComponent(); metroLabel1.Text = name; metroTextBox1.Text = info; metroTextBox1.Select(0, 0); try { Trace.WriteLine(Directory.GetCurrentDirectory() + "\\\\" + a.Picture); pictureBox1.Image = Image.FromFile(Directory.GetCurrentDirectory() + "\\\\" + a.Picture); } catch { MessageBox.Show("There was an error loading the image for this achievement."); } } }
而且如上所述,有很多重复项,但没有一个对我的回答有所帮助。 我对委托/调用过程也不了解,这就是为什么我需要一些额外的帮助。
更新#1
我queue.Add(m);
任何地方。 queue.Add(m);
现在已替换为queue.Enqueue(a);
我的更新方法(到目前为止没有计时器)只是:
public void DisplayDialog()
{
Achievement a = null;
queue.TryDequeue(out a);
MetroTaskWindow m = new MetroTaskWindow(a, this, a.Type.ToString(), new TaskWindowControl(a.Name, a.Message, a), 4, r, ((ScreenRegion)r).getGS());
m.Show();
}
我发现的是,当我将代码更改为此时,在MetroTaskWindow上执行动画和操作的Thread未被激活。 Windows停留在Windows加载循环中,并且永远不会消失。 有任何想法吗? 我正在使用OnActivated事件,所以当我.Show();时应该触发
编辑#2
为了使上述错误起作用,我最终要做的是将所有代码切换到新的Windows Form Timer。 这每5秒轮询一次,并防止由于while循环而导致MetroTaskWindow挂起。
Form1.Designer.cs
private System.Windows.Forms.Timer timer2;
timer2 = new System.Windows.Forms.Timer(this.components);
timer2.Interval = 5250;
timer2.Tick += new System.EventHandler(this.timer2_Tick);
Form1.cs的
private void timer2_Tick(object sender, EventArgs e)
{
if (queue.Count > 0)
{
DisplayDialog();
}
}
该错误是有道理的。 队列包含在工作线程而不是UI线程上创建的 MetroTaskWindow
实例。 您可以在后台工作程序的入口点方法中调用checkNotifications
,该方法在单独的线程上运行。
我建议您做的是:
MetroTaskWindow
类创建一个新类。 将此类的实例添加到checkNotifications
的队列中。 从外观上看,您可以直接使用Achievement
类并将其推送到队列。 CheckAvailable_DoWork
,您现在在其中调用queue[0].Show();
CheckAvailable_DoWork
。 解决此问题后,您应该发布一个新问题,即如何将代码重构为异步/等待并摆脱丑陋的后台工作人员。
快速重构的想法
我将完全删除第二个后台工作者,并使用计时器,而不是每X秒(或5250ms,如果您愿意)轮询队列。
要进行此工作,您需要将队列的类型(实际上是List<T>
更改为ConcurrentQueue<T>
。 这将允许您从工作程序中推送内容,并从UI线程中弹出(在计时器回调中)。
这样,您可以删除第二个背景工作者和while(true)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.