我应该这样使用ConcurrentQueue还是单独的线程?
本文关键字:单独 线程 ConcurrentQueue 我应该 | 更新日期: 2023-09-27 18:14:15
我正在做的是美化邮件合并,然后将文件转换为PDF…基于。net 4.5,我看到了几种实现线程的方法。使用线程安全队列似乎很有趣(计划a),但我可以看到一个潜在的问题。你觉得呢?我会尽量长话短说,但把需要的都写进去。
这是在假设数据库处理比PDF转换花费更多时间的情况下工作的。
在这两种情况下,每个文件的数据库处理都是在自己的线程/任务中完成的,但PDF转换可以在许多单个线程/任务中完成(计划B),或者可以在单个长时间运行的线程中完成(计划a)。这就是我想知道的PDF转换。这都是在一个try/catch语句,但线程不能失败或全部失败(计划a)。你认为这是一个好主意吗?如有任何建议,不胜感激。
/* A class to process a file: */
public class c_FileToConvert
{
public string InFileName { get; set; }
public int FileProcessingState { get; set; }
public string ErrorMessage { get; set; }
public List<string> listData = null;
c_FileToConvert(string inFileName)
{
InFileName = inFileName;
FileProcessingState = 0;
ErrorMessage = ""; // yah, yah, yah - String.Empty
listData = new List<string>();
}
public void doDbProcessing()
{
// get the data from database and put strings in this.listData
DAL.getDataForFile(this.InFileName, this.ErrorMessage); // static function
if(this.ErrorMessage != "")
this.FileProcessingState = -1; //fatal error
else // Open file and append strings to it
{
foreach(string s in this.listData}
...
FileProcessingState = 1; // enum DB_WORK_COMPLETE ...
}
}
public void doPDFProcessing()
{
PDFConverter cPDFConverter = new PDFConverter();
cPDFConverter.convertToPDF(InFileName, InFileName + ".PDF");
FileProcessingState = 2; // enum PDF_WORK_COMPLETE ...
}
}
/*** These only for Plan A ***/
public ConcurrentQueue<c_FileToConvert> ConncurrentQueueFiles = new ConcurrentQueue<c_FileToConvert>();
public bool bProcessPDFs;
public void doProcessing() // This is the main thread of the Windows Service
{
List<c_FileToConvert> listcFileToConvert = new List<c_FileToConvert>();
/*** Only for Plan A ***/
bProcessPDFs = true;
Task task1 = new Task(new Action(startProcessingPDFs)); // Start it and forget it
task1.Start();
while(1 == 1)
{
List<string> listFileNamesToProcess = new List<string>();
DAL.getFileNamesToProcessFromDb(listFileNamesToProcess);
foreach(string s in listFileNamesToProcess)
{
c_FileToConvert cFileToConvert = new c_FileToConvert(s);
listcFileToConvert.Add(cFileToConvert);
}
foreach(c_FileToConvert c in listcFileToConvert)
if(c.FileProcessingState == 0)
Thread t = new Thread(new ParameterizedThreadStart(c.doDbProcessing));
/** This is Plan A - throw it on single long running PDF processing thread **/
foreach(c_FileToConvert c in listcFileToConvert)
if(c.FileProcessingState == 1)
ConncurrentQueueFiles.Enqueue(c);
/*** This is Plan B - traditional thread for each file conversion ***/
foreach(c_FileToConvert c in listcFileToConvert)
if(c.FileProcessingState == 1)
Thread t = new Thread(new ParameterizedThreadStart(c.doPDFProcessing));
int iCount = 0;
for(int iCount = 0; iCount < c_FileToConvert.Count; iCount++;)
{
if((c.FileProcessingState == -1) || (c.FileProcessingState == 2))
{
DAL.updateProcessingState(c.FileProcessingState)
listcFileToConvert.RemoveAt(iCount);
}
}
sleep(1000);
}
}
public void startProcessingPDFs() /*** Only for Plan A ***/
{
while (bProcessPDFs == true)
{
if (ConncurrentQueueFiles.IsEmpty == false)
{
try
{
c_FileToConvert cFileToConvert = null;
if (ConncurrentQueueFiles.TryDequeue(out cFileToConvert) == true)
cFileToConvert.doPDFProcessing();
}
catch(Exception e)
{
cFileToConvert.FileProcessingState = -1;
cFileToConvert.ErrorMessage = e.message;
}
}
}
}
计划A似乎是一个很好的解决方案,但如果任务失败了怎么办?是的,PDF转换可以用单独的线程完成,但是我想保留它们用于数据库处理。
这是在一个文本编辑器中作为我能写的最简单的代码写的,所以可能会有一些东西,但我想我已经明白了。
你正在处理多少个文件?10 ?100000年?如果数量非常大,使用1个线程为每个文件运行DB查询不是一个好主意。
线程是一种非常低级的控制流结构,我建议你尽量避免在你的应用程序代码中大量混乱和详细的线程生成、连接、同步等。如果可以的话,尽量保持简单。
这样做如何:将每个文件所需的数据放在线程安全队列中。为结果创建另一个线程安全队列。生成一定数量的线程,这些线程重复地从输入队列中提取条目,运行查询,转换为PDF,然后将输出推入输出队列。线程应该只共享输入和输出队列。
你可以选择任何你喜欢的工作线程的数量,或者实验看看什么是一个好的数字。不要为每个文件创建一个线程——只选择一个允许良好的CPU和磁盘利用率的数字。
或者,如果你的语言/库有并行映射操作符,使用它。