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