使用多线程使用 iTextSharp 生成 Datamatrix 会导致崩溃

Posted

技术标签:

【中文标题】使用多线程使用 iTextSharp 生成 Datamatrix 会导致崩溃【英文标题】:Generating Datamatrix with iTextSharp using multithreading causes crash 【发布时间】:2021-09-23 13:50:39 【问题描述】:

为标签生成数据矩阵时,我的代码崩溃了。经过大量测试,我确定这是由于多线程造成的,但我无法确定原因。

我正在使用 iTextSharp v5.5.13.2(Nuget 包)。将复制错误的代码如下:

        for(int i = 0; i < 10; i++)
        
            ThreadPool.QueueUserWorkItem(x =>
            
                iTextSharp.text.pdf.BarcodeDatamatrix dataMatrix = new iTextSharp.text.pdf.BarcodeDatamatrix();

                dataMatrix.Height = 18;
                dataMatrix.Width = 18;
                dataMatrix.ForceSquareSize = true;

                dataMatrix.Generate("TestData");
            );
            Console.WriteLine(i);
        

这是错误:

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
This exception was originally thrown at this call stack:
    iTextSharp.text.pdf.BarcodeDatamatrix.B256Encodation(byte[], int, int, byte[], int, int, int, int, int)
    iTextSharp.text.pdf.BarcodeDatamatrix.GetEncodation(byte[], int, int, byte[], int, int, int, bool)
    iTextSharp.text.pdf.BarcodeDatamatrix.Generate(byte[], int, int)
    iTextSharp.text.pdf.BarcodeDatamatrix.Generate(string)
    DataAccessTesting.Form1.Function1_Click.AnonymousMethod__59_0(object) in Form1.cs
    System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(object)
    System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
    System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, object, bool)
    System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
    System.Threading.ThreadPoolWorkQueue.Dispatch()
    ...
    [Call Stack Truncated]

如果我删除多线程,它运行得非常好。所以这段代码有效:

        for (int i = 0; i < 10; i++)
        
            iTextSharp.text.pdf.BarcodeDatamatrix dataMatrix = new iTextSharp.text.pdf.BarcodeDatamatrix();

            dataMatrix.Height = 18;
            dataMatrix.Width = 18;
            dataMatrix.ForceSquareSize = true;

            dataMatrix.Generate("TestData");
            Console.WriteLine(i);
        

       

我在应用程序中使用多线程来生成所有标签并立即开始打印它们,即使其他标签仍在生成。强迫他们等到其他人完成处理会显着降低应用程序的速度,因此删除多线程是最后的选择。

如何在不移除多线程的情况下防止此错误发生?

编辑: 我一直被告知我没有完整的代码。我再次测试,通过将我添加的代码放入按钮事件中,我可以复制错误。这是一个屏幕截图。

【问题讨论】:

在 Winforms 应用程序和控制台应用程序中尝试,无论是否使用线程,都无法重现错误。您确定要显示所有相关代码吗? @evilmandarine 是的,我确定。我将此代码放在 WinForms 应用程序的按钮事件中。我可能会删除这个问题,因为我们使用了另一个似乎可以工作的库。另外,现在我们推出许可证时无需支付许可证费用。 @NicholaiRen - 代码不完整。要发生此错误,您必须在 ThreadPool.QueueUserWorkItem lambda 中使用 i。您的问题是您没有捕获局部变量。不过,您必须发布完整的代码才能获得答案。 @NicholaiRen - 抱歉,我刚看到截图。显然您的代码是完整的,这意味着组件中必须有一些static 状态,以防止多个实例在不同的线程中运行。存在固有的竞争条件。 @Enigmativity 奇怪的是,我现在可以重现该问题。确实有静态字段:private static int[][] f; private static int[][] switchMode;。使这些不是静态的并使类中的某些方法不是静态的可以解决问题:pastebin.com/PFnqBYQa。不确定我可以将其发布为答案。这个其他库也很旧,但效果很好:github.com/msmuelle-astrumit/DataMatrix.net. 【参考方案1】:

反编译库中的BarcodeDatamatrix 确认Generate 方法不是线程安全的,因为使用了以下字段(感谢@Enigmativity 的富有洞察力的评论):

private static int[][] f;
private static int[][] switchMode;

以下位置的类使这些字段(以及使用它们的其他方法)不是static,从而解决了线程安全问题。重要提示:如果库/NuGet(实际上是此类)得到更新,如果您想使用最新版本,可能需要再次执行此操作:https://pastebin.com/PFnqBYQa

替代解决方案 使用可用的DataMatrix.net 库here 或here(旧库,不确定哪个更好)。

其他信息 我还测试了 ZXing.NET,但 FNC1 字符(例如组分隔符 \x1D)无法正常工作。这适用于上述库。

代码示例(用于替代库):

// ZXING: FNC1 not working
//var dm = new BarcodeWriter
//
//    Format = BarcodeFormat.DATA_MATRIX,
//    Options =
//    
//        GS1Format = true,
//        PureBarcode = true
//    
//;

//var gs1Code = "0107612345678900171" + "\x1D" + "00503";
//var bmp = dm.Write(gs1Code);
//return bmp;


// Datamatrix.NET
var imageEncoder = new DmtxImageEncoder();
var options = new DmtxImageEncoderOptions

    ModuleSize = 8,
    MarginSize = 30,
    BackColor = Color.White,
    ForeColor = Color.Black,
    Scheme = DmtxScheme.DmtxSchemeAsciiGS1
;

var barcode = txt1.Text.Length == 13 
    ? "0" + txt1.Text
    : txt1.Text;

return imageEncoder.EncodeImage("01" + barcode
                              + "10" + txt2.Text + "\x1D"
                              + "21" + txt3.Text
                              , options);

【讨论】:

以上是关于使用多线程使用 iTextSharp 生成 Datamatrix 会导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

使用iTextSharp生成的PDF生成水印给出错误

如何使用 iTextSharp 为横向生成的页面添加页脚到 PDF 文档

acrofields不使用多页文档打印 - itextsharp

使用 iTextSharp 将文本添加到内存流中的现有多页 PDF 文档

iTextSharp 从现有的 PDF 模板生成 PDF

iTextSharp 正在生成损坏的 PDF