我有一组打印不同类型文档的线程类。这些类使用继承来共享公共代码。类构造函数需要文件名和打印机名参数。方法创建一个Print()
新的工作线程,等待工作线程完成使用Thread.Join(timeout)
,如果超时则调用Thread.Abort()
工作线程Join
。工作线程启动一个可以打开指定文件的应用程序,使文件同步发送到打印机(通常使用应用程序的Print方法)并退出。工作线程的代码被包装在一个try{} ... catch{}
块中,以处理任何不可预见的外部应用程序崩溃。catch 块包含最少的清理和日志记录。
internal static FilePackage TryPrintDocumentToPdf(string Filename)
{
.....
Logging.Log("Printing this file using PowerPoint.", Logging.LogLevel.Debug);
printableFormat = true;
fc = new FileCollector(Email2Pdf.Settings.Printer.PdfAttachmentCollectDirectoryObj, FileCollector.CollectMethods.FileCount | FileCollector.CollectMethods.FilesNotInUse | FileCollector.CollectMethods.ProcessExit);
fc.FileCount = 1;
fc.ProcessNames = new string[] { OfficePowerPointExe, Email2Pdf.Settings.Printer.PrinterExe };
fc.Prepare();
using (PowerPointPrinter printer = new PowerPointPrinter(Filename, Email2Pdf.Settings.Printer.PdfAttachmentPrinter))
{
printer.KillApplicationOnClose = true;
printer.Print();
printOk = printer.PrintOk;
}
.....
}
internal abstract class ApplicationPrinter : IDisposable
{
protected abstract string applicationName { get; }
protected string filename;
protected string printer;
protected bool workerPrintOk;
protected bool printOk;
public bool PrintOk { get { return printOk; } }
public bool KillApplicationOnClose { get; set; }
public void Print()
{
System.Threading.Thread worker = new System.Threading.Thread(printWorker);
DateTime time = DateTime.Now;
worker.Start();
if (worker.Join(new TimeSpan(0, Email2Pdf.Settings.Printer.FileGenerateTimeOutMins, 0)))
{
printOk = workerPrintOk;
}
else
{
worker.Abort();
printOk = false;
Logging.Log("Timed out waiting for " + applicationName + " file " + filename + " to print.", Logging.LogLevel.Error);
}
}
protected abstract void Close();
protected abstract void printWorker();
public virtual void Dispose() { Close(); }
}
internal class PowerPointPrinter : ApplicationPrinter
{
private const string appName = "PowerPoint";
protected override string applicationName { get { return appName; } }
private Microsoft.Office.Interop.PowerPoint.Application officePowerPoint = null;
public PowerPointPrinter(string Filename, string Printer)
{
filename = Filename;
printer = Printer;
this.Dispose();
}
protected override void printWorker()
{
try
{
officePowerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
officePowerPoint.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
Microsoft.Office.Interop.PowerPoint.Presentation doc = null;
doc = officePowerPoint.Presentations.Open(
filename,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
doc.PrintOptions.ActivePrinter = printer;
doc.PrintOptions.PrintInBackground = Microsoft.Office.Core.MsoTriState.msoFalse;
doc.PrintOptions.OutputType = Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides;
doc.PrintOut();
System.Threading.Thread.Sleep(500);
doc.Close();
//Marshal.FinalReleaseComObject(doc);
doc = null;
workerPrintOk = true;
}
catch (System.Exception ex)
{
Logging.Log("Unable to print PowerPoint file " + filename + ". Exception: " + ex.Message, Logging.LogLevel.Error);
Close();
workerPrintOk = false;
}
}
protected override void Close()
{
try
{
if (officePowerPoint != null)
officePowerPoint.Quit();
Marshal.FinalReleaseComObject(officePowerPoint);
officePowerPoint = null;
if (KillApplicationOnClose)
Utility.KillProcessesByName(OfficePowerPointExe);
}
catch { }
}
}
我发现我的应用程序没有响应,主线程在 Thread.Abort() 行的 Sleep/Wait/Join 中。我不记得工作线程的状态,但是应该在catch{}
块中执行的日志记录没有发生。(在我发现它没有响应后,我用 VS2010附加到我的进程中)。
我参考了Thread.Abort 方法中的以下注释:
如果正在中止的线程位于代码的受保护区域(例如 catch 块、finally 块或受约束的执行区域)中,则调用 Abort 的线程可能会阻塞。如果调用 Abort 的线程持有被中止线程所需的锁,则可能发生死锁。
我相信我有一个死锁问题,因为(1)它并不总是发生,(2)因为MSDN上的注释(上图)。
- 注意似乎暗示如果线程可以被'ed使用,则在线程内使用
try{} ... catch{}
是永远不会安全的。Abort()
这是真的? - 我看不出如何避免
Abort()
在我的场景中使用。改用Thread.Interrupt()
会有什么不同吗? - 如何解决我遇到的死锁问题?
BackgroundWorker 对我不起作用,因为我不需要进度报告,更重要的是,我的工作线程可能会在执行第三方应用程序时无限期阻塞。出于同样的原因,我不能要求我的线程终止,而只有一个选择 - 无情地Abort()
让工作线程。