我使用Quartz.NET库创建了Windows Service应用程序,以便为报告目的安排作业。应用程序的主要部分是从不同位置的数据库中获取一些数据(~260),因此我决定使用Parallel.ForEach在中心位置并行获取和存储数据。
在Quartz.NET Job中,我从我的实用程序类中运行静态方法,进行并行处理。
公用事业类:
public class Helper
{
public static ConcurrentQueue<Exception> KolekcijaGresaka = new ConcurrentQueue<Exception>(); // Thread-safe
public static void Start()
{
List<KeyValuePair<string, string>> podaci = Aktivne(); // List of data for processing (260 items)
ParallelOptions opcije = new ParallelOptions { MaxDegreeOfParallelism = 50 };
Parallel.ForEach(podaci, opcije, p =>
{
UzmiPodatke(p.Key, p.Value, 2000);
});
}
public static void UzmiPodatke(string oznaka, string ipAdresa, int pingTimeout)
{
string datumTrenutneString = DateTime.Now.ToString("d.M.yyyy");
string datumPrethodneString = DatumPrethodneGodineString();
string sati = DateTime.Now.ToString("HH");
// Ping:
Ping ping = new Ping();
PingReply reply = ping.Send(ipAdresa, pingTimeout);
// If is online call method for copy data:
if (reply.Status == IPStatus.Success)
{
KopirajPodatke(oznaka, ipAdresa, datumTrenutneString, datumPrethodneString, sati, "TBL_DATA");
}
}
public static void KopirajPodatke(string oznaka, string ipAdresa, string datumTrenutneString, string datumPrethodneString, string sati, string tabelaDestinacija)
{
string lanString = "Database=" + ipAdresa + "://DBS//custdb.gdb; User=*******; Password=*******; Dialect=3;";
IDbConnection lanKonekcija = new FbConnection(lanString);
IDbCommand lanCmd = lanKonekcija.CreateCommand();
try
{
lanKonekcija.Open();
lanCmd.CommandText = "query ...";
DataTable podaciTabela = new DataTable();
// Get data from remote location:
try
{
podaciTabela.Load(lanCmd.ExecuteReader());
}
catch (Exception ex)
{
throw ex;
}
// Save data:
if (podaciTabela.Rows.Count > 0)
{
using (SqlConnection sqlKonekcija = new SqlConnection(Konekcije.DB("Podaci")))
{
sqlKonekcija.Open();
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(sqlKonekcija))
{
bulkcopy.DestinationTableName = tabelaDestinacija;
bulkcopy.BulkCopyTimeout = 5; // seconds
bulkcopy.ColumnMappings.Add("A", "A");
bulkcopy.ColumnMappings.Add("B", "B");
bulkcopy.ColumnMappings.Add("C", "C");
bulkcopy.ColumnMappings.Add("D", "D");
try
{
bulkcopy.WriteToServer(podaciTabela);
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}
catch (Exception ex)
{
KolekcijaGresaka.Enqueue(ex);
}
finally
{
lanCmd.Dispose();
lanKonekcija.Close();
lanKonekcija.Dispose();
}
}
应用程序大部分时间都在工作(作业每天执行4次),但有时会卡住并挂起(通常在处理~200项并行时),从而阻塞主线程并永不停止。 似乎来自并行处理的线程之一被阻止并阻止主线程的执行。这可能是死锁引起的吗?
如何确保没有一个线程阻止应用程序执行(即使没有成功获取数据)?上面的代码有什么问题?
答案 0 :(得分:0)
如何确保没有一个线程阻止应用程序执行(即使没有成功获取数据)?上面的代码有什么问题?
Parallel.Foreach不是异步的,它只是并行执行每次迭代,所以它会在继续之前等待每个操作完成。如果你真的不想在返回调用者之前等待所有操作完成,那么尝试使用Task工厂来安排这些操作并默认使用线程池。
即
foreach(var p in podaci)
{
Task.Factory.StartNew(() => UzmiPodatke(p.Key, p.Value, 2000));
}
或者使用ThreadPool.QueueUserWorkItem或BackgroundWorker,无论您熟悉什么,并且适用于您想要的行为。
这可能无法解决您的所有问题,只是无法响应的程序。最有可能的是,如果您的代码确实存在问题,那么您的一个任务最终会抛出一个异常,如果未处理则会导致程序崩溃。或者更糟糕的是,如果任务永远不会完成,那么你只会坐在那里占用资源。然而,可能只是偶尔其中一个需要非常长的情况。在这种情况下,您可以根据需要处理此问题(取消长任务,确保所有先前计划的任务在安排更多之前完成,等等),并且任务并行库可以通过一些小的修改来支持所有这些情况。