如何使全局对象线程安全

本文关键字:线程 安全 对象 何使全 | 更新日期: 2023-09-27 18:10:22

所有,我有一个大型应用程序的并发性问题,我最近多线程。问题出在可以运行批处理作业的脚本处理器中

public async Task<Result> ProcessScriptAsync(
    CancellationTokenSource cancelSource,
    TaskScheduler uiScheduler)
{
    ...
    // Get instance of active workbook on UI thread.
    IWorkbook workbook = this.workbookView.ActiveWorkbook;
    while (notFinished)
    {
        ...
        Task<bool> runScriptAsyncTask = null;
        runScriptAsyncTask = Task.Factory.StartNew<bool>(() =>
        {
            return RunScript(ref workbook);
        }, this.token,
           TaskCreationOptions.LongRunning,
           TaskScheduler.Default);
        // Some cancellation support here...
        // Run core asynchroniously.
        try
        {
            bGenerationSuccess = await runScriptAsyncTask;
        }
        catch (OperationCanceledException)
        {
            // Handle cancellation.
        }
        finally 
        {
            // Clean up.
        }
    }
...
}   

当你考虑RunScript方法时,我的问题就来了。传递给RunScript的对象不是线程安全的,而是在UI线程上创建的。因此,我必须在RunScript方法中创建这个对象的"深度拷贝"…

private bool RunScript(ref IWorkbook workbook)
{
    ...
    // Get a new 'shadow' workbook with which to operate on from a background thread.
    IWorkbook shadowWorkbook;
    if (File.Exists(workbook.FullName)) 
    {
        // This opens a workbook from disk. The Factory.GetWorkbook method is thread safe.
        shadowWorkbook = SpreadsheetGear.Factory.GetWorkbook(workbook.FullName); // (##)
    }
    else
        throw new FileNotFoundException(
            "The current workbook is not saved to disk. This is a requirement. " + 
            "To facilitate multi-threading!");
    // Do hard work here...
    shadowWorkbook.WorkbookSet.GetLock();
    try
    {
        // Do work and add worksheets to shadowWorkbook.
    }
    finally
    {
        // Reassign the UI workbook object to our new shadowWorkbook which 
        // has been worked on. This is fine to do as not work is actually being 
        // done on workbook.
        workbook = shadowWorkbook;
        shadowWorkbook.WorkbookSet.ReleaseLock();
    }
}

我的问题在用(##)标记的线上。每次执行RunScript时,它都会从磁盘创建一个新的shadowWorkbook。这样做的问题是,一些工作表是在shadowWorkbook中创建的,随后在处理结束时复制回workbook。但是,每次执行RunScript时,我都会从磁盘中获取工作簿,该磁盘在最后一个循环中没有生成新的工作表。

我已经研究了使shadowWorkbook成为一个全局对象,但它是在UI线程上创建的,因此不能随后从我的后台操作中使用。解决这个问题的一种方法是在每次创建工作表后将工作簿保存到磁盘,但是有很多创建,这将是昂贵的。

是否有一种方法可以使shadowWorkbook全局线程安全,使我能够在线程调用中持久化对IWorkbook的更改?

感谢您的宝贵时间。

如何使全局对象线程安全

嗯,我想说在这种情况下,this.workbookView.ActiveWorkbook的可变资源需要是线程安全的。你说的工作簿对象是在UI线程上创建的,因此当你分配workbook = shadowWorkbook时,你会在那里和任务线程中为它争夺。

可以声明一个同步对象,例如:

private static Object _objectLock = new Object();

和使用这样在你的RunScript方法(和其他任何地方你的工作簿被修改),以确保从不同的线程串行访问资源:

lock(_objectLock)
{
    workbook.AddWorkSheet();
}

在这种情况下,您不需要深度复制工作簿,因此删除了昂贵的调用和它所创建的所有额外的复杂性。

如果我理解正确,你应该做的是为每个workbook创建一个Thread。在那个Thread上,创建你的shadowWorkbook,然后运行一个循环,处理对那个工作簿的请求,使用类似BlockingCollection<Action<IWorkbook>>的东西。

你可以这样写

workbookManager.Run(workbook, w => /* do work and add worksheets to w */);