gpt4 book ai didi

c# - 在主线程上使用大型全局对象时,如何不从工作线程中阻止主UI线程

转载 作者:行者123 更新时间:2023-12-03 13:00:15 33 4
gpt4 key购买 nike

我正在使用用于证件扫描器的外部库,为简单起见,我将其称为ScanLib。扫描过程很长,当然,在调用诸如ScanLib.Scan()之类的方法时,它往往会阻塞UI线程,因此我认为这是使用Tasks的绝佳时机。这里是用于上下文的简化(或TLDR)代码(下面的完整代码以获取更多详细信息,但为了节省时间,此处进行了总结):

public partial class MainForm : Form () 
{
//Here I initialize the reference variable which I will use to work with the Scanner library
ScanLib scanLibraryReference = new ScanLib();

// Form constructor
public Form()
{
//This initializes the scanner and it's library components, it runs until the program closes
Task scannerInitTask = Task.Run(() => scanLibraryReference.InitializeScanLibrary())
InitializeComponent();
}

private void Button_Click(object sender, EventArgs e) {
Task scannerTask = Task.Run(() => scanLibraryReference.ScanCard());
}
}

问题是,当我尝试扫描卡时,它冻结了主UI线程,即使很难,我也在其他任务中同时运行ScanLib.InitializeScanLib()和ScanLib.ScanCard()方法以尝试不阻塞主UI线程,因为后者很耗时。我已经阅读了主要UI线程阻塞的原因,并且我认为这可能是两件事之一:
  • 我正在使用全局声明的scanLibraryReference变量来使用该库,甚至很难在任务中使用它,因为在变量上声明了它,所以在使用它时它可能会阻塞主UI线程。
  • 根据文档,所有ScanLib方法都可能引发许多错误,这些错误以数字形式出现(错误1001、1002、1003等),为了简化错误记录,文档要求我声明+100常量,类似于:public const int SCANLIB_ERR_SCANFAILED = 1001;。所有这些“错误常量”都在MainForm的public部分公共(public)类的另一个文件中声明,因此也许使用其他任务(线程)中的这些常量可能会冻结主UI线程

  • 这些是我的主要嫌疑犯,但话虽如此,您可能会认为我会解决,但我没有解决,这就是我的问题所在:
    在程序的整个过程中,我将一直需要ScanLib引用,但是如果在主UI线程上创建它,则会被阻塞。我可以尝试创建一个新任务,例如:
    任务backgroundWorker = Task.Run(()=> {ScanLib scanLibRef =新的ScanLib(); scanLibRef.InitializeLibrary()});
    但是据我了解,此变量现在将存在于此线程中,并且不能在另一个线程中使用,或者可以吗?甚至,即使我创建了一个简单的线程来仅容纳变量,该线程也将在声明完变量后死亡。我只是想通过Thread函数来做到这一点,但是随之而来的一个问题是,当我按下一个按钮并为其提供功能来运行扫描仪时,如何将其重新激活。谁能提出一个解决方案,说明如何声明需要不断使用的全局变量而不阻塞主UI线程?

    请求完整的代码(很长,很抱歉,所有Console.WriteLines都用于调试)
        using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using ScannerTest;
    using System.Threading;

    namespace ScannerTest
    {
    public partial class Form1 : Form
    {
    // 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\";

    public Form1()
    {
    Task initTask = Task.Run(() => InitScanLibraries());
    InitializeComponent();
    Console.WriteLine("\nForm initialized succesfully.");
    }

    #region Button events

    // The normal way of scanning which of course blocks the main UI thread
    private void ScanCardNonAsync_Click(object sender, EventArgs e)
    {
    Console.WriteLine("");
    Console.WriteLine("*********************");
    Console.WriteLine("Starting new scan...");
    string currentScan = ImageSource + "MyScan.bmp";
    string modifiedScan = ImageSource + "MyEditedScan.bmp";
    ScanCard(currentScan, modifiedScan);
    OCRscan();
    GetOCRData();
    Console.WriteLine("Scan finalized..");
    Console.WriteLine("*********************");
    }

    // My attempt at scanning asynchronously which still blocks the UI thread
    private void ScanCardAsync_Click(object sender, EventArgs e)
    {
    Console.WriteLine("");
    Console.WriteLine("*********************");
    Console.WriteLine("Starting new scan...");
    string currentScan = ImageSource + "MyScan.bmp";
    string modifiedScan = ImageSource + "MyEditedScan.bmp";
    // Here I chain the methods in a task chain to scan a Card
    Task ScannerStart = new Task(() => ScanCard(currentScan, modifiedScan));
    Task ScannerStep2 = ScannerStart.ContinueWith((x) => OCRscan());
    Task ScannerStep3 = ScannerStep2.ContinueWith((y) => GetOCRData());
    ScannerStart.Start();
    }

    #endregion

    #region Methods

    private void InitScanLibraries()
    {
    switch (scanSlibxEx.InitLibrary("49B2MFWE8WUJXLBW"))
    {
    case SLIB_ERR_SCANNER_BUSSY:
    System.Console.WriteLine("ERROR: Scanner Busy...");
    break;
    case LICENSE_VALID:
    System.Console.WriteLine("");
    System.Console.WriteLine("**********************************");
    System.Console.WriteLine("SlibxEx initialized succesfully...");
    break;
    case LICENSE_INVALID:
    System.Console.WriteLine("ERROR: License Invalid");
    break;
    case LICENSE_EXPIRED:
    System.Console.WriteLine("ERROR: License Expired");
    break;
    case SLIB_ERR_DRIVER_NOT_FOUND:
    System.Console.WriteLine("ERROR: Driver not found");
    break;
    case SLIB_ERR_SCANNER_NOT_FOUND:
    System.Console.WriteLine("ERROR: Scanner not found");
    break;
    }

    switch (scanIdData.InitLibrary("49B2MFWE8WUJXLBW"))
    {
    case SLIB_ERR_SCANNER_BUSSY:
    System.Console.WriteLine("ERROR: Scanner Busy...");
    break;
    case LICENSE_VALID:
    System.Console.WriteLine("License validation succesful...");
    break;
    case LICENSE_INVALID:
    System.Console.WriteLine("ERROR: License Invalid");
    break;
    case LICENSE_EXPIRED:
    System.Console.WriteLine("ERROR: License Expired");
    break;
    case SLIB_ERR_DRIVER_NOT_FOUND:
    System.Console.WriteLine("ERROR: Driver not found");
    break;
    case SLIB_ERR_SCANNER_NOT_FOUND:
    System.Console.WriteLine("ERROR: Scanner not found");
    break;
    case GENERAL_ERR_PLUG_NOT_FOUND:
    System.Console.WriteLine("ERROR: Attatched scanner is not one of the following:\n ScanShell 600 \n ScanShell 800 \n ScanShell1000");
    break;
    case SLIB_LIBRARY_ALREADY_INITIALIZED:
    System.Console.WriteLine("ERROR: Call ignored, library already initialized");
    break;
    }

    }

    private void ScanCard(string ImagePath, string ModifiedImagePath)
    {
    Console.WriteLine("Attempting scan...");

    switch (scanSlibxEx.ScanToFile(ImagePath))
    {
    case SLIB_ERR_NONE:
    Console.WriteLine("Scan succesful...");
    break;
    case SLIB_ERR_SCANNER_BUSSY:
    Console.WriteLine("ERROR: Scanner is busy...");
    break;
    case LICENSE_INVALID:
    Console.WriteLine("ERROR: License invalid");
    break;
    case SLIB_ERR_SCANNER_NOT_FOUND:
    Console.WriteLine("ERROR: Scanner not found");
    break;
    case SLIB_ERR_SCANNER_GENERAL_FAIL:
    Console.WriteLine("ERROR: Scanner general fail");
    break;
    case SLIB_ERR_HARDWARE_ERROR:
    Console.WriteLine("ERROR: Hardware error");
    break;
    case SLIB_ERR_PAPER_FED_ERROR:
    Console.WriteLine("ERROR: Paper fed error");
    break;
    case SLIB_ERR_SCANABORT:
    Console.WriteLine("ERROR: Scan aborted");
    break;
    case SLIB_ERR_NO_PAPER:
    Console.WriteLine("ERROR: No paper");
    break;
    case SLIB_ERR_PAPER_JAM:
    Console.WriteLine("ERROR: Paper jammed");
    break;
    case SLIB_ERR_FILE_IO_ERROR:
    Console.WriteLine("ERROR: File I/O error");
    break;
    case SLIB_ERR_PRINTER_PORT_USED:
    Console.WriteLine("ERROR: Printer port used");
    break;
    case SLIB_ERR_OUT_OF_MEMORY:
    Console.WriteLine("ERROR: Out of memory");
    break;
    }
    //scanCImage.RotateImage(ImageSource, 90, 1, ModifiedImagePath);

    }

    private void OCRscan()
    {
    Console.WriteLine("Attempting OCR extraction...");
    string data = "";
    int region = scanIdData.AutoDetectState(data);
    // Check for card region
    switch (region)
    {
    case ID_ERR_USA_TEMPLATES_NOT_FOUND:
    Console.WriteLine("ERROR: No USA templates found");
    break;
    case INVALID_INTERNAL_IMAGE:
    Console.WriteLine("ERROR: No internal image loaded");
    break;
    case ID_ERR_STATE_NOT_SUPORTED:
    Console.WriteLine("ERROR: State not supported");
    break;
    case ID_ERR_STATE_NOT_RECOGNIZED:
    Console.WriteLine("ERROR: State not recognized");
    break;
    default:
    Console.WriteLine("Region catch succesful");
    break;
    }

    // Begin OCR extraction
    string data2 = "";
    Console.WriteLine("Attempting data extraction...");
    switch (scanIdData.ProcState(data2, region))
    {
    case ID_TRUE:
    Console.WriteLine("Data extraction succesful.");
    break;
    case LICENSE_INVALID:
    Console.WriteLine("ERROR: LICENSE_INVALID");
    break;
    case SLIB_ERR_SCANNER_NOT_FOUND:
    Console.WriteLine("ERROR: SLIB_ERR_SCANNER_NOT_FOUND. ");
    break;
    case SLIB_ERR_INVALID_SCANNER:
    Console.WriteLine("ERROR: SLIB_ERR_INVALID_SCANNER. ");
    break;
    case ID_ERR_STATE_NOT_SUPORTED:
    Console.WriteLine("ERROR: ID_ERR_STATE_NOT_SUPORTED. ");
    break;
    case INVALID_INTERNAL_IMAGE:
    Console.WriteLine("ERROR: INVALID_INTERNAL_IMAGE. ");
    break;
    default:
    Console.WriteLine("ERROR: Uncatched exception in Form1.OCRScan()");
    break;
    }
    // Data copying to local
    Console.WriteLine("Copying data locally...");
    if (scanIdData.RefreshData() != 0) Console.WriteLine("Data copied succesfully."); else Console.WriteLine("ERROR: Problem while copying data");
    }

    private void GetOCRData()
    {
    //loc* Variables are locally declared global variables, while the scanIdData.* are library variables where OCR scan results are saved
    Console.WriteLine("Saving data locally...");
    locName = scanIdData.Name;
    locNameFirst = scanIdData.NameFirst;
    locNameMiddle = scanIdData.NameMiddle;
    locNameLast = scanIdData.NameLast;
    locNameSuffix = scanIdData.NameSuffix;
    locID = scanIdData.Id;
    locLicense = scanIdData.license;
    locIssueDate = scanIdData.IssueDate;
    locAddress = scanIdData.Address;
    locExperationDate = scanIdData.ExpirationDate;
    locCSC = scanIdData.CSC;
    locCity = scanIdData.City;
    locEyes = scanIdData.Eyes;
    locDup_Test = scanIdData.Dup_Test;
    locState = scanIdData.State;
    locHair = scanIdData.Hair;
    locEndorsements = scanIdData.Endorsements;
    locZip = scanIdData.Zip;
    locHeight = scanIdData.Height;
    locFee = scanIdData.Fee;
    locCounty = scanIdData.County;
    locClass = scanIdData.Class;
    locRestriction = scanIdData.Restriction;
    locDateOfBirth = scanIdData.DateOfBirth;
    locSex = scanIdData.Sex;
    locSigNum = scanIdData.SigNum;
    locType = scanIdData.Type;
    locWeight = scanIdData.Weight;
    locAddress2 = scanIdData.Address2;
    locAddress3 = scanIdData.Address3;
    locAddress4 = scanIdData.Address4;
    locAddress5 = scanIdData.Address5;
    locText1 = scanIdData.Text1;
    locText2 = scanIdData.Text2;
    locText3 = scanIdData.Text3;
    Console.WriteLine("Data saved succesfully.");
    }

    #endregion
    }
    }

    编辑

    我按照Onur的建议做了,是的, 阻止主UI线程的是全局变量scan Lib 。我运行了以下代码,它没有冻结主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,在Form构造函数中,我添加了一个新方法名为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;
    }
    //...
    }

    关于c# - 在主线程上使用大型全局对象时,如何不从工作线程中阻止主UI线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25064454/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com