[英]How to Make a Global Object Thread-Safe
总而言之,我最近有一个多线程的大型应用程序存在并发问题。 问题出在可以运行批处理作业的脚本处理器中
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
。 这样做的问题是在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 */);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.