当前位置: 首页 > 编程语言 > C#

在C#中,两个线程之间传递数据的推荐方式是什么?分享

时间:2023-04-10 11:15:39 C#

在C#中,两个线程之间传递数据的推荐方式是什么?我有我的主GUI线程,第二个线程在它自己的ApplicationContext中运行(即使没有工作可做,它也能保持活动状态)。我想从我的GUI线程在我的第二个线程上调用一个方法,但是如果我只调用thread.Method();它似乎在我的主GUI线程上运行并导致我的GUI变得无响应。在不同线程上调用方法的最佳方式是什么?更新:我真正想做的是在两个线程之间进行通信,而不是与GUI进行通信。GUI恰好是需要与我的第二个线程通信的线程之一。更新#2:好的,我一定是错过了什么。我创建了一个事件和一个委托,并让我的工作线程订阅了该事件。但是当我调用Invoke(MyEvent)时;在我的GUI线程中,工作线程最终在GUI线程上工作并挂起GUI线程,直到它完成处理。如果不轮询静态对象,我要做什么?.Net已经附带了一个System.ComponentModel.BackgroundWorker类,旨在处理执行后台任务和与GUI通信。用它。哇,我不敢相信人们怎么会懒得去读这个问题。不管怎样,那是我的工作。创建一个“消息”类。这将存储您要共享的所有信息。为每个线程创建一个队列。使用SyncLock(C#锁)对其进行读/写。当您想与线程对话时,通过将消息添加到队列,向它发送一个包含所需信息副本的消息对象。然后工作线程可以从队列中读取,按顺序读取和处理每条消息。没有消息的时候,就睡觉吧。确保不要在两个线程之间共享对象。一旦您的GUI线程将消息放入队列中,GUI线程就不再拥有该消息。它不能保留对消息的引用,否则你会遇到麻烦。这不会给你最好的性能,但它对大多数应用程序来说已经足够了。更重要的是,它使犯错变得更加困难。更新:不要使用SyncLock和队列。而是使用ConcurrentQueue,它将自动为您处理任何锁定。您将获得更好的表现并且不太可能犯错误。实际上,您已经创建了一个穷人版本的ThreadPool。你的第二个主题就是坐在那里什么都不做,你没有足够的工作量,你无法让它为你工作。您必须将委托传递给您的线程然后起飞并执行的队列。最好的办法是按照您的意愿进行操作,只使用.NET线程池。伙计,阅读Albahari关于.Net线程的免费电子书。我以任何方式连接到它,所以它不是插头。我读过它,让我的同事读过它,而且我已经多次使用它。我建议创建一个生产者/消费者类,您可以在其中启动一个等待(非阻塞)线程、排队任务和发出开始工作的信号。只是谷歌它。我假设GUI中的某些事件需要一些长时间运行的任务在后台运行——有两种主要方法可以做到这一点。如果您只想调用不同线程上的方法,可以通过异步调用同步方法来实现。我通常做这样的事情://delegatewithsameprototypeasthemethodtocallasynchronouslydelegatevoidProcessItemDelegate(objectitem);//异步调用的方法privatevoidProcessItem(objectitem){...}//GUI线程中的方法privatevoidDoWork(objectitemToProcess){//创建异步调用的委托...ProcessItemDelegated=newProcessItemDelegate(this.ProcessItem);IAsyncResultresult=d.BeginInvoke(itemToProcess,newAsyncCallback(this.CallBack)Method),d;}//异步操作完成时调用的方法privatevoidCallbackMethod(IAsyncResultar){ProcessItemDelegated=(ProcessItemDelegate)ar.AsyncState;//必须在任何异步调用的委托上调用EndInvoke!d.EndInvoke(ar);使用此方法时,请注意回调是在后台线程上执行的,因此对GUI的任何更新都必须使用Invoke完成。或者,您可以使用共享状态在线程之间进行通信,并使用EventWaitHandle来指示对共享状态的更新——在本例中,是GUI中的一种方法,用于将工作项添加到队列中以在后台进行处理。工作线程在工作可用时处理队列中的项目。//共享状态私有队列workQueue;私有EventWaitHandle事件句柄;//在gui线程中运行的方法privatevoidDoWork(ItemitemToProcess){//使用私有锁对象而不是锁...lock(this.workQueue){this.workQueue.Add(itemToProcess);这个.eventHandle.Set();}}//在后台线程上运行的方法privatevoidQueueMonitor(){while(keepRunning){//如果事件句柄未发出信号,处理线程将在这里休眠,直到发出信号或超时到期if(this.eventHandle.WaitOne(optionalTimeout)){lock(this.workQueue){while(this.workQueue.Count>0){ItemitemToProcess=this.workQueue.Dequeue();//对项目做一些事情...}}//重置等待句柄-注意AutoResetEvent自动重置this.eventHandle.Reset();}}}Control.BeginInvoke()的方便性很难传递。你不一定。在项目中添加一个新类型并粘贴以下代码:usingSystem;使用系统线程;使用System.Windows.Forms;publicpartialclassfrmWorker:Form{publicfrmWorker(){//启动工作线程Threadt=newThread(newParameterizedThreadStart(WorkerThread));t.IsBackground=true;t.开始(这个);}publicvoidStop(){//同步线程停止this.Invoke(newMethodInvoker(stopWorker),null);}privatevoidstopWorker(){this.Close();}privatestaticvoidWorkerThread(objectfrm){//启动消息循环frmWorkerf=frmasfrmWorker;f.CreateHandle();应用程序运行(f);}protectedoverridevoidSetVisibleCore(boolvalue){//不应该变得可见value=false;base.SetVisibleCore(值);这是一些测试它的示例代码:publicpartialclassForm1:Form{privatefrmWorkermWorker;publicForm1(){InitializeComponent();mWorker=newfrmWorker();}privatevoidbutton1_Click(objectsender,EventArgse){Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);mWorker.BeginInvoke(新方法调用者(RunThisOnThread));}privatevoidRunThisOnThread(){Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);}privatevoidbutton2_Click(objectsender,EventArgse){mWorker.Stop();使用同步对象通知线程它需要处理新数据(或GUI的新状态)。一种相对简单的方法是使用事件对象。这是它如何工作的概要:GUI线程第二个线程共享一个事件对象(所以他们都知道它)第二个线程通常在某种循环中运行,并在每次需要第二个线程时等待事件发出信号当做某事,GUI线程发出事件信号当第二个线程完成时,它重置事件并再次等待(或退出)在第二个线程中放置一个循环,该线程大部分时间都处于休眠状态,但每个[Interval]它都会唤醒并检查一个共享变量,告诉它是否运行您的方法,如果该共享布尔值设置为true,则它运行一个方法来执行您尝试执行的任何任务……在该方法中,let方法收集来自另一个共享变量的所需数据。在主GUI线程中,将数据放入方法参数共享变量中,然后将bool“Run”共享变量设置为true...在worker方法中,记得在完成后将共享bool“run”变量重置为false,这样循环就不会一遍又一遍地运行同一个实例......您可以使用事件或Grauenwolf所说的-消息提示。我将每个线程包装为一个管理单例,您可以从那里轻松实现。你甚至可以做穷人的公共财产翻转位。也可以实现一个状态机,不用传递消息,每个线程可以互相监听以上是C#学习教程:在C#中,两个线程之间传递数据的推荐方式是什么?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: