访问主线程变量时线程挂起 [英] Thread hangs when accessing mainthread variables

查看:115
本文介绍了访问主线程变量时线程挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写应用程序,此代码部分是不起作用的部分的简化版本...
该应用程序将是使用UIAutomation操作UI的Windows窗体应用程序.
Mainthread和eventhandling线程也可以操纵UI,因此在事件处理期间,我想挂起mainthread以不具有UI的并发用法.
也许使用的方法不是最好的...但是我不知道如何以不同的方式做到这一点.
我的问题是,对于事件处理,我正在使用一个线程,并且当我想在for语句中到达主线程的静态变量时,该线程将挂起.
这个简化的版本设法访问它,但是在迭代过程中挂起,我的实际应用程序在第一次迭代时挂起...
任何帮助将不胜感激...
在此先谢谢您,
B.

I am coding an application and this codepart is the simplified version of the not working part...
The app will be a Windows Form application manipulating UI using UIAutomation.
Mainthread and eventhandling thread manipulates the UI as well so during event handling i want to suspend the mainthread not to have concurrent usage of the UI.
Maybe the used method is not the best...but i have no idea how to do this in different way.
My problem would be that for event handling i am using a thread and this thread hangs when i want to reach mainthread''s static variables in the for statement.
This simplified version manages to access it but hangs during the iteration, my real application hangs for the first iteration...
Any help would be appreciated...
Thanks in advanced,
B.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using System.Threading;
using Microsoft.Win32;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Net.Mail;
using System.Net;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Media;

namespace TestEHWin
{
    internal static class Worker
    {
        internal static AutomationElement eventSenderElement = null;
        internal static List<AutomationElement> dialogs = new List<AutomationElement>();

        internal static Thread mainThread;
        internal static string debugFile = @"C:\Test_logging.txt";
        internal static string debugFileEvent = @"C:\Test_logging_Event.txt";

        internal static void Start()
        {
            StreamWriter sw = new StreamWriter(Worker.debugFileEvent);
            sw.Close();

            int _counter = 0;
            mainThread = Thread.CurrentThread;
            DebugWrite("M - Mainthread id: " + mainThread.ManagedThreadId);

            dialogs = GetChildDialogs(AutomationElement.RootElement);

            foreach (AutomationElement _item in dialogs)
            {
                DebugWrite("M - Dialog: " + _item.Current.Name);
            }

            Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Children, new StructureChangedEventHandler(strucChangedHandler));

            DateTime stop = DateTime.Now.AddSeconds(20);
            do
            {
                _counter++;
                DebugWrite("M - Iteration number #" + _counter.ToString());
                Sleep(10);
            } while (DateTime.Now < stop);

            Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, strucChangedHandler);

            MessageBox.Show("Finished....");
        }

        private static void strucChangedHandler(object _sender, StructureChangedEventArgs _e)
        {
            AutomationElement _senderElement = _sender as AutomationElement;
            Object _windowPattern;

            if (_e.StructureChangeType == StructureChangeType.ChildAdded)
            {
                if (false == _senderElement.TryGetCurrentPattern(WindowPattern.Pattern, out _windowPattern))
                {
                    return;
                }

                eventSenderElement = _senderElement;
                System.Threading.Thread _thread = new Thread(new ThreadStart(testHandler));
                _thread.Priority = ThreadPriority.Highest;
                _thread.Start();
            }
        }

        private static void testHandler()
        {
            mainThread.Suspend();
            DebugWriteEvent("EHT - Thread #" + mainThread.ManagedThreadId.ToString() + " will be suspended.");
            DebugWriteEvent("EHT - ThreadID: " + Thread.CurrentThread.ManagedThreadId);

            DebugWriteEvent("EHT - New dialog name: " + eventSenderElement.Current.Name);

            dialogs = GetChildDialogs(AutomationElement.RootElement);
            DebugWriteEvent("EHT - Dialogs number: #" + dialogs.Count.ToString());

            for (int i = 0; i < dialogs.Count; i++)
            {
                DebugWriteEvent("EHT - Dialog#" + (i + 1).ToString() + " in root: " + dialogs[i].Current.Name);
                Thread.Sleep(100);
            }

            DebugWriteEvent("EHT - Thread #" + mainThread.ManagedThreadId.ToString() + " will be alive again.");
            mainThread.Resume();
        }

        internal static void Sleep(int timeoutInMs)
        {
            TimeSpan wait = DateTime.Now.TimeOfDay;
            wait = wait.Add(new TimeSpan(0, 0, 0, 0, timeoutInMs));

            while (DateTime.Now.TimeOfDay < wait)
            {
                Application.DoEvents();
            }
        }

        internal static List<AutomationElement> GetChildDialogs(AutomationElement _basedialog)
        {
            List<AutomationElement> _dialoglist = new List<AutomationElement>();
            TreeWalker _walker = new TreeWalker(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
            AutomationElement _node = _walker.GetFirstChild(_basedialog);
            while (_node != null)
            {
                _dialoglist.Add(_node);
                _node = _walker.GetNextSibling(_node);
            }
            return _dialoglist;
        }

        internal static void DebugWrite(string _text)
        {
            StreamWriter sw = File.AppendText(Worker.debugFile);
            sw.WriteLine(DateTime.Now.ToString("HHmmss_fffffff: ") + _text + " Thread ID: " + Thread.CurrentThread.ManagedThreadId.ToString());
            sw.Flush();
            sw.Close();
            return;
        }

        internal static void DebugWriteEvent(string _text)
        {

            StreamWriter sw = File.AppendText(Worker.debugFileEvent);
            sw.WriteLine(DateTime.Now.ToString("HHmmss_fffffff: ") + _text + " Thread ID: " + Thread.CurrentThread.ManagedThreadId.ToString());
            sw.Flush();
            sw.Close();
            return;
        }
    }
}


固定代码格式


Fixed code formatting

推荐答案

我发现很难回答这个问题以使答案有所帮助.
我将尝试以这种方式进行说明:这种设计多线程代码的尝试失败了.请,请帮忙,不要尝试改善它.您将需要放弃它并开始从头开始编写正确的代码,但是要帮助您,我需要知道应该完成什么以及为什么.

现在,我只能解释什么地方不对,并提供一些参考资料,可以为您提供更好的帮助.

主要问题是您尝试使用基于旋转等待的自制方法来同步线程.如果您认为可以使用非线程技术并将其应用于线程,请再考虑一下.通过"if","while"等类似的组合,您将无法使用无法通过不使用OS调用实现的线程同步原语.当您等待系统事件时,OS会关闭线程并将其保持在特殊的等待状态,并且永远不要将其调度回执行直到唤醒,因此线程使用的CPU时间严格为零.您只需让线程运行以等待常规条件即可.这不是多线程.这种等待的最接近的多线程模拟是EventWaitHandle.

这样的一件坏事是Application.DoEvents.由于某种原因,这是一个常见的谬论.根据经验法则,永远不要使用它.至于在另一个线程中等待DoEvents,这是一种永远等待的方式,因为您可以不断地以永无止境的方式发送消息.

每次Sleep调用都没有意义.您睡了10毫秒;为什么?您希望某些事情会改变.

另一个不好的标志是ManagedThreadId标识线程.为什么?不需要这样做.另一个不好的标志是更改线程优先级.您真的不需要它.如果您的逻辑是错误的,那么优先级将无法改善.想一想.通过提高优先级,您不会使任何事情变得更快,而只会使其他线程更频繁地屈服.您为什么认为它可以提高吞吐量?

您的代码看起来对于任务来说太复杂了.这是我无法理解所有目的的原因之一.如果几乎是只有一个或两个问题的工作代码,可能的话.在这种情况下,几乎所有事情都是错误的.因此,我无法为您提供代码.同样,我确定您应该重新开始.

只是几个想法.

要使用UI,请不要从非UI线程调用任何UI方法或属性.您无法从非UI线程调用与UI相关的任何操作.相反,您需要使用System.Windows.Threading.DispatcherInvokeBeginInvoke方法(对于Forms或WPF)或System.Windows.Forms.Control(仅对于Forms).

在我过去的答案中,您将找到有关其工作原理的详细说明和代码示例:
Control.Invoke()与Control.BeginInvoke() [ ^ ],
Treeview Scanner和MD5的问题 [如何获取keydown事件在vb.net中的不同线程上操作 [启用禁用+多线程后控件事件不会触发 [ ^ ].

—SA
I find it difficult to answer this question to make the answer helpful.
I''ll try to put it in this way: this attempt to design multithreading code failed. Please, do your favor and don''t try to improve it. You will need abandon it and start writing correct code from scratch, but to help you I would need to know what should be accomplished and why.

For now, I can only explain what''s wrong and give you some references which could give your better ides.

The main problem is your attempt to synchronize thread using home-baked method based on spin wait. If you think that you can use non-threading techniques and apply them to thread, think again. You fail to use thread synchronization primitives which cannot be implemented not using OS calls, by combination of "if" "while" and the like. When you wait for system events, OS switches your thread off and keeps it in a special wait state and never schedule it back to execution until waken up, so the thread uses strictly zero CPU time. You simply keep the thread running waiting for a regular condition. This is not multithreading. The closest multithreading analog of such wait is EventWaitHandle.

One such bad thing is Application.DoEvents. By some reason, this is a common fallacy. You should never use it, as a matter of thumb rule. As to waiting for DoEvents in another thread, this is a way to wait forever, as you can keep pumping messages the way they never end.

Each and every Sleep call makes no sense. You sleep 10 ms; why? Do you hope something will change.

One more bad sign is identification of threads by ManagedThreadId. Why? There is no need to do this. Another bad sign is changing thread priority. You really never need it. If your logic is wrong, priority cannot improve thing. Think about it. By raising priority you don''t make anything faster, you only make other threads yielding to you thread more often. Why do you think it improves throughput?

You code looks too complex for the task. This is one reason I cannot understand the purpose of all this. If would be possible if it was almost working code with just one or two problems. In this case, nearly everything is wrong. So, I cannot help you with your code. Again, I am sure you should start it over.

Just few ideas.

To work with UI, you never call any UI methods or properties from non-UI thread. 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[^].

—SA


感谢您的回答!
代码的环境"没有您所提到的那么详细,因此我将提供一些有关它的更多信息.我同意您的观点,该代码不太好,我不是C#的专家,只是一个热心的业余爱好者... :-)

因此,此简单代码应执行以下操作:
-简单的表格带有一个按钮
-按下按钮会导致Worker.Start()方法调用
-主线程启动并运行到20秒,结构更改事件处理已添加到自动化树根目录中为新创建的对话框中.
-因此,当新程序启动时(例如,浏览器),事件处理应该运行,而主线程不应该执行任何操作(这就是为什么我使用这种简单的日志记录来检查在哪里发生了什么……).但是如果没有线程使用也是可能的.
-因为我的应用程序中存在UI操纵,所以使用了sleep,然后由于UI中的某些结果而需要进行睡眠.我因为睡眠而使用了Application.DoEvents,如果线程睡眠的时间太长,则无法处理传入事件进入垃圾箱(至少我没有设法做到)
-因此,如果此代码(或其他代码)应该这样做,则主体将冻结"直到事件处理完成...

还有一点我的应用程序(.net 3.5 sp1):
-WinForm应用程序-使用表单是因为其余代码的用户输入处理
-Mainthread应该做某事(在这种简化中,每10毫秒记录一次).在应用程序中,它正在通过UIAutomation处理其他程序(打开程序的对话框,处理其控件,例如,...因此基本上是通过C#代码模拟程序的用法).假设mainthread处理"A"程序.为了正确起见,UIVerifier工具(UIAutomation帮助器)看不到"A"程序的每个控件,因此需要使用鼠标定位和用法来处理控件(添加了一些图像识别以使控件处于位置...)
-事件处理部分应处理UIAutomation根目录中的新对话框(例如:处理新启动的程序).假设这部分处理"B"程序.再次出现一些无法识别的控件问题...
-应该暂停Mainthread,因为不能并行使用"A"程序和"B"程序.并行使用可能导致误点击和其他一些问题.

同时,我正在检查您链接的资源.
也许EventWaitHandle会完成我需要的工作...
在此先谢谢您,
B.
Thanks for your answer!
The "environment" of the code was not too detailed as you mentioned so I would give some further information about it. I agree your point of view that this code is not too good, I am not an expert of C#, just an enthusiastic amateur... :-)

So this simplyfied code should do the following:
- simple form comes up with a single button
- pushing the button causes Worker.Start() method invoking
- the mainthread starts and runs till 20 seconds, structure changes event handling added for newly created dialogs in the root of automation tree.
- so when a new program started (eg: a browser) the event handling should run meanwhile mainthread should not do anything (that is why I used this simple logging to check what is happening where...). But if it is possible without thread usage should be good as well.
- sleep is used beacuse in my app there is UI manipulation and then sleeps are required because of some results in the UI. I used Application.DoEvents because of sleeps, if a thread is sleeped too long then an incoming event goes to trash can not be handled (at least i have not managed to)
- so if this code (or some other one) should do that the mainpart "freezes" till eventhandling finishes...

And a little bit from my application (.net 3.5 sp1):
- WinForm application - form is used because of user input handling for the rest of the code
- Mainthread should do something (in this simplification logging in every 10ms). In the app it is doing some other programs handling via UIAutomation (open dialogs of a program, handle its controls, eg... so basically emulating the program usage via C# code). Let''s say mainthread handles "A" program. For sake of correctness UIVerifier tool (UIAutomation helper) can not see each control of "A" program so mouse positioning and usage is needed for handling controls (some image recognision is added to have controls position...)
- Event handling part should do the handling of the new dialogs in UIAutomation root (eg: handling of newly started program). Let''s say this part handles "B" program. Again some problems with not recognised controls...
- Mainthread should be suspended because "A" program and "B" program can not be used parallel. Parallel usage can cause misclicks and some other problems.

Meanwhile I am checking the resources you linked.
Maybe EventWaitHandle will do the work I would need...
Thanks in advanced,
B.


这篇关于访问主线程变量时线程挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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