【问题标题】:How to Make a Global Object Thread-Safe如何使全局对象线程安全
【发布时间】:2013-04-23 22:00:47
【问题描述】:

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

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 进行持久更改?

感谢您的宝贵时间。

【问题讨论】:

  • 也许Volatile 就是你要找的东西
  • @noobob 也许不是。 volatile 在这种特定情况下会做什么?它应该应用于哪个领域?
  • 您是说要同时从多个线程修改文件吗?恐怕没有简单的方法可以做到这一点。
  • 不,我知道这是不可能的。我遇到的问题与每次我的后台线程需要工作时都必须创建 workbook 对象的新实例有关。在批处理过程中,多次调用RunScript 方法,并且每次完成shadowWorkbook 的工作 - 问题是我无法在调用运行脚本之间保留对shadowWorkbook 的更改而不执行“硬”保存(即写入磁盘)这是昂贵的。我想知道如何使shadowWorkbook 全局化,而不会引起跨线程问题...

标签: c# winforms task-parallel-library async-await


【解决方案1】:

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

也许声明一个同步对象,例如:

private static Object _objectLock = new Object();

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

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

在这种情况下,您不需要深度复制您的工作簿,因此消除了昂贵的调用及其产生的所有额外复杂性。

【讨论】:

  • +1 感谢您的想法,但这对我不起作用。当您说“this.workbookView.ActiveWorkbook 中的可变资源在这种情况下需要是线程安全的”时,很好,但事实并非如此,这是第三方库。 workbook(UI 线程拥有)对象的分配不是问题,shadowWorkbook 的持久性可以从后台线程重复使用(无需线程上下文切换)。
【解决方案2】:

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

你可以通过写类似的东西来使用它

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

【讨论】:

  • 为这个想法喝彩。对IWorkbook 对象执行的操作必须是顺序的;目前尚不清楚这种方法是否可以解决我的问题,在每个线程上我仍然会遇到问题,即在每个线程启动时我需要创建一个 IWorkbook 对象并以线程安全的方式执行此操作需要使用shadowWorkbook = SpreadsheetGear.Factory.GetWorkbook(workbook.FullName); 在相关线程上打开一个实例 - 希望在执行之间坚持使用 shadowWorkbook。我目前已添加保存以解决此问题...
  • +1 为您提供帮助。非常感谢。考虑到这一点,使用我目前的设计似乎不太可能......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-06
  • 2013-12-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-24
  • 2016-05-15
相关资源
最近更新 更多