Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在主线程上处理大型全局对象时,如何不从工作线程阻止主UI线程_C#_Multithreading_Com_Thread Safety_Task - Fatal编程技术网

C# 在主线程上处理大型全局对象时,如何不从工作线程阻止主UI线程

C# 在主线程上处理大型全局对象时,如何不从工作线程阻止主UI线程,c#,multithreading,com,thread-safety,task,C#,Multithreading,Com,Thread Safety,Task,我正在使用一个卡扫描仪的外部库,为了保持简单,我将其称为ScanLib。扫描过程很长,当然,它在调用ScanLib.Scan()等方法时会阻塞UI线程,因此我认为这将是使用任务的最佳时机。这里是上下文的简化(或TLDR)代码(下面的完整代码了解更多详细信息,但为了节省时间,这里对其进行了总结): 问题是,当我尝试扫描一张卡时,它会冻结主UI线程,甚至很难在其他任务中运行ScanLib.InitializeScanLib()和ScanLib.ScanCard()方法,以尝试不阻止主UI线程,因为后

我正在使用一个卡扫描仪的外部库,为了保持简单,我将其称为ScanLib。扫描过程很长,当然,它在调用ScanLib.Scan()等方法时会阻塞UI线程,因此我认为这将是使用任务的最佳时机。这里是上下文的简化(或TLDR)代码(下面的完整代码了解更多详细信息,但为了节省时间,这里对其进行了总结):

问题是,当我尝试扫描一张卡时,它会冻结主UI线程,甚至很难在其他任务中运行ScanLib.InitializeScanLib()和ScanLib.ScanCard()方法,以尝试不阻止主UI线程,因为后者很耗时。我已经了解了主UI线程阻塞的原因,我认为这可能是两件事之一:

  • 我使用全局声明的scanLibraryReference变量来使用库,即使我在任务中使用它,它也可能在使用它时阻塞主UI线程,因为该变量是在它上面声明的
  • 根据文档,所有ScanLib方法都会抛出许多数字错误(错误100110021003等),为了简化错误记录,文档要求我声明大约+100个常量,类似于:public const int ScanLib_ERR_scanfiled=1001;。所有这些“错误常量”都在MainForm的public partial class类型的另一个文件中声明,因此从另一个任务(线程)使用这些常量可能会冻结主UI线程
  • 这些是我的主要嫌疑犯,但话说回来,你会认为我会解决它,但我没有,这就是我的问题所在: 在整个程序运行期间,我将不断需要ScanLib引用,但是如果我在主UI线程上创建它,它就会被阻塞。我可以尝试创建一个新任务,如: Task backgroundWorker=Task.Run(()=>{ScanLib scanLibRef=new ScanLib();scanLibRef.InitializeLibrary()}); 但据我所知,这个变量现在将存在于这个线程上,不能从另一个线程使用,或者可以吗?或者,即使我创建了一个简单的线程来存放变量,该线程在声明变量后也会死掉。我曾经考虑过用线程函数来实现这一点,但接下来的问题是,当我按下一个按钮时,如何调用它,并为它提供一个函数来运行扫描器。有谁能提出一个解决方案,说明如何声明一个需要经常使用的全局变量而不阻塞主UI线程

    请求完整代码(很抱歉,如果代码太长,则所有Console.WriteLines都用于调试)

    编辑

    我按照Onur的建议做了,是的,阻止主UI线程的是全局变量扫描库。我运行了以下代码,但没有冻结主UI线程:

    Task debugTask = Task.Run(() =>
            {
                // All 4 variables below are global and used extensively to call scanner methods
                // Main Scan basic functions Library
                NetScanW.SLibEx scanSlibxEx = new NetScanW.SLibEx();
                // Main Scan extended functios Library
                NetScanW.CImage scanCImage = new NetScanW.CImage();
                // Main Scan OCR functions library
                NetScanW.IdData scanIdData = new NetScanW.IdData();
                // Main Scan extended functions 2 library
                NetScanWex.SLibEx scanWexSlibxEx = new NetScanWex.SLibEx();
                string ImageSource = @"C:\Scans\";
                string currentScan = ImageSource + "MyScan.bmp";
                string modifiedScan = ImageSource + "MyEditedScan.bmp";
                InitScanLibraries(scanSlibxEx, scanIdData);
                ScanCard(currentScan, modifiedScan, scanSlibxEx);
            });
    
    是的,非常非常凌乱,但它起作用了,没有冻结。我所做的只是声明全局变量,初始化库并在同一线程中运行扫描,当然,它没有冻结主UI线程,但这远远不是我想要的。我需要这些库保持初始化状态,在辅助线程中运行,当我需要扫描某个东西时,让它从ScanLib引用变量调用ScanLib方法,因为我不知道把它放在哪里,所以它不会阻止主UI线程。我将从Onur尝试下面的答案,看看会发生什么

    最终编辑

    为了完成我的问题,我想添加已解决的代码,以防其他人需要它。根据Orun的回答,我没有在顶部声明全局变量,比如ScanLib refScanLib=new ScanLib(),而是将它们声明为null对象,比如:ScanLib refScanLib=null,在表单构造函数中,我添加了一个名为InitializeVariables()的新方法,该方法执行以下操作:

    public void InitializeVariables()
        {
            NetScanW.SLibEx scanSLibExx = null;
            NetScanW.IdData scanIdDataa = null;
            NetScanW.CImage scanCImagee = null;
            NetScanWex.SLibEx scanWexSLibExx = null;
            var th = new Thread(() => 
            {
                scanSLibExx = new NetScanW.SLibEx();
                scanIdDataa = new NetScanW.IdData();
                scanCImagee = new NetScanW.CImage();
                scanWexSLibExx = new NetScanWex.SLibEx();
            });
            th.SetApartmentState(ApartmentState.MTA);
            th.Start();
            th.Join();
            this.scanSlibxEx = scanSLibExx;
            this.scanIdData = scanIdDataa;
            this.scanCImage = scanCImagee;
            this.scanWexSlibxEx = scanWexSLibExx;
        }
    

    在这之后,一切都很顺利。我还没有完全理解它,但它是有效的,谢谢大家的帮助。

    在使用COM库的类似情况下,这对我来说是有效的

    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]            // <= I needed to add this attribute
        private static void Main()
        {
           //...
        }
    }
    
    
    
    
    public partial class MainForm : Form () 
    {    
        // you can call this in the InitializeComponents() for instance
        void someMethodInYourFormIERunningOnTheUIThread()
        {
            ScanLib scanLib  = null;
            var th = new Thread(() =>
            {
                scanLib = new ScanLib();
            });
            th.SetApartmentState(ApartmentState.MTA); // <== this prevented binding the UI thread for some operations
            th.Start();
            th.Join();
            this.scanLibraryReference = scanLib;
        }
        //...
    }
    
    内部静态类程序
    {
    /// 
    ///应用程序的主要入口点。
    /// 
    [statthread]//
    {
    scanLib=新的scanLib();
    });
    
    th.SetApartmentState(ApartmentState.MTA);//全局声明的变量不应该是问题所在,它们是否在UI线程上声明并不重要。我认为我们缺少了更多的代码,因为从这里看很好。在后台还有什么事情没有发布吗?也许发布你的
    ScanCard
    方法在做什么我简化了代码ost我可以,因为我不想把每个人都搞糊涂。大多数ScanLib方法都是隐藏的,所以我所能做的就是向它提供数据。我将在下面的一秒钟发布完整的代码,以获得更广泛的上下文。在UI被阻止时暂停调试器。当前堆栈显示谁在阻止UI。知道什么窗口/工具栏显示了这一点吗?我有一个simil使用
    COM
    的库存在ar问题。问题是由于
    scanLibraryReference
    是在UI线程上创建的,因此与
    COM
    相关的内容以某种方式绑定到UI线程。在不同的线程/任务中创建
    scanLibraryReference
    可能会有所帮助。我还必须添加
    [STAThread]
    属性到
    程序.Main
    方法。我按照您之前的建议做了,并用测试结果编辑了我的帖子。是的,您是对的,阻止UI线程的是那些全局变量,尽管将其全部放在一个新任务中进行演示,但仍然让我想知道如何让它保持活动状态以访问这些变量s、 不管是哪种方式,我现在就尝试一下,希望看看会发生什么,我不知道STAthread或SetApartmentState()做什么,但让我们看看我通过示例学到了什么。谢谢。哇……它奏效了……我还是不敢相信。我没有
    public void InitializeVariables()
        {
            NetScanW.SLibEx scanSLibExx = null;
            NetScanW.IdData scanIdDataa = null;
            NetScanW.CImage scanCImagee = null;
            NetScanWex.SLibEx scanWexSLibExx = null;
            var th = new Thread(() => 
            {
                scanSLibExx = new NetScanW.SLibEx();
                scanIdDataa = new NetScanW.IdData();
                scanCImagee = new NetScanW.CImage();
                scanWexSLibExx = new NetScanWex.SLibEx();
            });
            th.SetApartmentState(ApartmentState.MTA);
            th.Start();
            th.Join();
            this.scanSlibxEx = scanSLibExx;
            this.scanIdData = scanIdDataa;
            this.scanCImage = scanCImagee;
            this.scanWexSlibxEx = scanWexSLibExx;
        }
    
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]            // <= I needed to add this attribute
        private static void Main()
        {
           //...
        }
    }
    
    
    
    
    public partial class MainForm : Form () 
    {    
        // you can call this in the InitializeComponents() for instance
        void someMethodInYourFormIERunningOnTheUIThread()
        {
            ScanLib scanLib  = null;
            var th = new Thread(() =>
            {
                scanLib = new ScanLib();
            });
            th.SetApartmentState(ApartmentState.MTA); // <== this prevented binding the UI thread for some operations
            th.Start();
            th.Join();
            this.scanLibraryReference = scanLib;
        }
        //...
    }