使代码线程安全 [英] Making Code Thread Safe

查看:68
本文介绍了使代码线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是C#线程的新手,我想帮助使这段代码线程安全。



线程AGetData ; 
public void Get()
{
SetA_TimeSteps.Items。明确();
SetA_InformationTree.Nodes [ 2 ]。节点[ 1 ]。Nodes.Clear();
Capabilities.Load( http://datapoint.metoffice.gov.uk/public/data/layer ?/ wxobs /所有/ XML /功能键= 14963e76-bad2-49ca-8c12-f47afd52deee);
for int x = 0 ; x < Capabilities.SelectNodes( /图层/图层[@ displayName ='降雨量'] /服务/时间/时间)。计数; x ++)
{SetA_TimeSteps.Items.Add(Capabilities.SelectNodes( / Layers / Layer [@ displayName ='Rainfall'] / Service / Times / Time)[x] .InnerText); }
SetA_LayerImage.LoadAsync(FUNCTIONS.CreateMapURL( obs RADAR_UK_Composite_Highres png,SetA_TimeSteps.Items [ 0 ]。ToString()));
SetA_TimeSteps.SelectedIndex = 0 ;
SetA_LastLive = SetA_TimeSteps.Items [ 0 ]。ToString();
SetA_LayerImage.Enabled = true ;
SetA_TimeSteps.Enabled = true ;
SetA_PrepareAnimation.Enabled = true ;
SetA_ExportMapData.Enabled = true ;
SetA_ExportDataOverlay.Enabled = true ;
SetA_ExportDataInformation.Enabled = true ;
foreach string item in SetA_TimeSteps.Items)
{SetA_InformationTree.Nodes [ 2 ]。节点[ 1 ]。 Nodes.Add(item.ToString()); }
SetA_InformationTree.ExpandAll();
}

// --- GetData_Click ---
private void SetA_GetData_Click( object sender,EventArgs e) // A ---
{
AGetData = new 线程(Get);
AGetData.Start();
Throbber.ShowDialog();
}





应该发生的事情是这样的:当我点击GetData按钮(SetA_GetData_Click)时,应该单独启动线程(AGetData)。原始线程应显示带有GIF动画的加载表单,告诉用户它正在加载,并应在新线程完成工作时关闭。新线程应设置控件上的所有属性,并在线获取xml文件中的资源。



主窗体在原始线程上,包含所有我想从新线程设置的控件。我不想使用BackgroundWorker。主要问题是如何使代码线程安全以及如何在新线程完成时关闭加载表单。我也会说这个帖子可以重用。希望这是有道理的,谢谢。

解决方案

重用一个线程是一个好主意。 (但请记住,另一种选择是使用线程池。)



如果你需要一个线程在整个时间段内重用在应用程序中,您可以在一开始创建一个应用程序,并在不需要时进行限制。你需要做的是让它处于等待状态。它将一个线程切换出来并且没有被调度回执行,直到它被唤醒,这发生在不同的条件下,特别是超时和中止。您可以使用类 System.Threading.ManualResetEvent

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx [ ^ ]。



当您将事件置于非信号状态并且某个线程调用其 WaitOne 方法时,它将使线程处于等待状态,并唤醒该线程当事件设置由其他一些线程发出信号时。我希望你有这样的想法:当你为这个主题做一些工作时,你就会发出信号。



另见我过去对这个主题的回答:

线程中的ManualResetEvent和AutoResetEvent [ ^ ],
暂停正在运行的主题 [ ^ ]。



您可能需要更高级别的此类机制的实现。将任务提供给线程的众所周知的方法是阻塞队列。您可以在我的文章中找到完整的源代码和实现: Simple Blocking Queue for线程通信和线程间调用 [ ^ ]。



使用.NET 4.0,您可以使用 Concurrent.BlockingCollection 相反,请参阅我的文章的替代方案:

http://www.codeproject.com/script/Articles/ListAlternatives.aspx?aid=149540 [ ^ ]。



请注意,使用.NET,您还可以提供委托实例并在它们被提供给线程时在另一个线程上调用它们。这是重用线程的最先进和最容易使用的方法。我的文章是完整的使用示例解释该技术。



此外,通过线程包装器使用线程是非常有益的。请看我过去的答案:

如何将ref参数传递给线程 [ ^ ],

更改线程(生产者)启动后的参数 [ ^ ],

C#中的多线程 [ ^ ]。



现在,与UI同步......奇怪的是,m机制与阻塞队列的解决方案非常相似,只有队列是非阻塞的:这是UI线程的事件队列。我将解释:



您无法从非UI线程调用与UI相关的任何内容。相反,您需要使用 Invoke System.Windows.Threading的方法。 Dispatcher (对于Forms或WPF)或 System.Windows.Forms.Control (仅限表单)。



您将在我过去的答案中找到有关其工作原理和代码示例的详细说明:

Control.Invoke()与Control.BeginInvoke() [ ^ ],

使用Treeview扫描仪和MD5的问题 [ ^ ]。



另请参阅有关线程的更多参考资料:

如何让keydown事件在不同的操作上运行vb.net中的线程 [ ^ ],

在启用禁用+多线程后控制事件未触发 [ ^ ]。



对于其他线程同步杂项,请使用常用的同步原语。但请记住:最佳同步不同步。不,我不是建议忽略线程安全,永远不要。相反,您应该以尽可能平行的方式设计代码:尝试使用函数样式,在堆栈上执行所有操作,避免静态数据等。换句话说,减少同步的需要。



-SA


嗨我认为你应该从这里开始在C#中进行思考 [ ^ ]

I am new to threading in C# and I would like some help with making this code thread-safe.

Thread AGetData;
        public void Get()
        {
            SetA_TimeSteps.Items.Clear();
            SetA_InformationTree.Nodes[2].Nodes[1].Nodes.Clear();
            Capabilities.Load("http://datapoint.metoffice.gov.uk/public/data/layer/wxobs/all/xml/capabilities?key=14963e76-bad2-49ca-8c12-f47afd52deee");
            for (int x = 0; x < Capabilities.SelectNodes("/Layers/Layer[@displayName='Rainfall']/Service/Times/Time").Count; x++)
            { SetA_TimeSteps.Items.Add(Capabilities.SelectNodes("/Layers/Layer[@displayName='Rainfall']/Service/Times/Time")[x].InnerText); }
            SetA_LayerImage.LoadAsync(FUNCTIONS.CreateMapURL("obs", "RADAR_UK_Composite_Highres", "png", SetA_TimeSteps.Items[0].ToString()));
            SetA_TimeSteps.SelectedIndex = 0;
            SetA_LastLive = SetA_TimeSteps.Items[0].ToString();
            SetA_LayerImage.Enabled = true;
            SetA_TimeSteps.Enabled = true;
            SetA_PrepareAnimation.Enabled = true;
            SetA_ExportMapData.Enabled = true;
            SetA_ExportDataOverlay.Enabled = true;
            SetA_ExportDataInformation.Enabled = true;
            foreach (string item in SetA_TimeSteps.Items)
            { SetA_InformationTree.Nodes[2].Nodes[1].Nodes.Add(item.ToString()); }
            SetA_InformationTree.ExpandAll();
        }

        // ---GetData_Click---
        private void SetA_GetData_Click(object sender, EventArgs e) //A---
        {
            AGetData = new Thread(Get);
            AGetData.Start();
            Throbber.ShowDialog();            
        }



What should happen is like this: When I click the GetData button (SetA_GetData_Click), it should start a separate thread (AGetData). The original thread should show a loading form with an animated gif on to tell the user it is loading and should close when the new thread has finished doing the work. The new thread should set all of the properties on the controls and get the resources from the xml files online.

The main form is on the original thread and contains all of the controls I want to set from a new thread. I don't want to use a BackgroundWorker. The main problem is how to make the code thread-safe and how to close the loading form when the new thread has finished. I also would lie this thread to be reusable. Hope this makes sense, thank you.

解决方案

It's a good idea to reuse a thread. (But remember that another option is to use the thread pool.)

If you need a thread to be reused during the full time of the application, you can create one at the very beginning and throttle when it is not needed. What you need to do is to put it in a wait state. It switched a thread out and not scheduled back to execution until it is awaken, which happens on different conditions, in particular, timeout and abort. You can use the class System.Threading.ManualResetEvent:
http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx[^].

When you put an event in a non-signaled state and some thread calls its WaitOne method, it will put the thread in a wait state, and awake that thread when the event is set signaled by some other thread. I hope you got the idea: you signal the event when you have some work for this thread to do.

See also my past answers on this topic:
ManualResetEvent and AutoResetEvent in Thread[^],
pause running thread[^].

You may need more high-level implementation of such mechanism. A well-known method of feeding tasks to a thread is a blocking queue. You can find full source code and implementation in my article: Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

With .NET 4.0, you can use Concurrent.BlockingCollection instead, please see the alternative to my article:
http://www.codeproject.com/script/Articles/ListAlternatives.aspx?aid=149540[^].

Note that with .NET you can also feed the delegate instances and invoke them on the other thread as they are fed to the thread. This is the most advanced and easiest to use method of reusing threads. My article is complete with usage samples explaining the technique.

Also, it is very beneficial to use a thread via a thread wrapper. Please see my past answers:
How to pass ref parameter to the thread[^],
Change parameters of thread (producer) after it is started[^],
MultiThreading in C#[^].

Now, synchronization with the UI… Oddly enough, the mechanism is very similar to the solution with blocking queue, only the queue is non-blocking: this is the event queue of a UI thread. I'll explain:

You cannot call anything related to UI from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

For other thread synchronization chores, use usual synchronization primitives. Remember though: best synchronization is no synchronization. No, I'm not suggesting ignoring thread safety, never. Instead, you should design the code in as parallel way as possible: try to use functional style, do everything on stack, avoid static data, and so on. In other words, reduce the need for synchronization.

—SA


Hi I think you should start here Threading in C#[^]


这篇关于使代码线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆