繁体   English   中英

Parallel.ForEach和阻塞线程

Parallel.ForEach and blocking thread

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我使用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个项目时),从而阻塞了主线程,并且永远不会结束。 似乎来自并行处理的线程之一被阻塞并阻止了主线程的执行。 这可能是由死锁引起的吗?

如何确保没有一个线程阻止应用程序执行(即使没有成功获取数据)? 上面的代码有什么问题?

1 个回复

如何确保没有一个线程阻止应用程序执行(即使没有成功获取数据)? 上面的代码有什么问题?

Parallel.Foreach不是异步的,它仅并行执行每个迭代,因此它将等待每个操作完成后再继续。 如果您真的不愿意等待所有操作完成再返回调用者,请尝试使用Task工厂计划这些操作并默认使用线程池。

foreach(var p in podaci)
{
    Task.Factory.StartNew(() => UzmiPodatke(p.Key, p.Value, 2000));
}

或使用ThreadPool.QueueUserWorkItem或BackgroundWorker,无论您熟悉或适用于所需的行为。

这可能无法解决您的所有问题 ,只是无法响应的程序。 最有可能的是,如果您的代码确实存在问题,那么您的一个任务最终将引发异常,如果未处理,它将导致程序崩溃。 更糟糕的是,如果任务永不结束,您将有“卡住”的任务只是坐在那里,浪费资源。 但是,可能只是偶尔出现其中一种情况花费极长时间的情况。 在这种情况下,您可以根据需要进行处理(取消长任务,确保在安排更多任务之前先完成所有先前安排的任务等),并且任务并行库可以通过稍作修改即可支持所有这些情况。

1 为什么使用Parallel.ForEach时主线程被阻塞

下面是我的代码: 因此Parallel.ForEach从ThreadPool以及现有的主线程中获取两个线程。 输出为: 线程1首先处理 线程3处理第二 线程4正在处理第三项 5秒后: 这是线程1 显然,线程1(主线程)被阻塞。 但是 ...

4 Parallel.ForEach 阻塞调用方法

我遇到了Parallel.ForEach问题。 我编写了一个简单的应用程序,将要下载的文件名添加到队列中,然后使用 while 循环遍历队列,一次下载一个文件,然后在下载文件后,调用另一个异步方法从下载的对象创建对象memoryStream 。 此方法的返回结果不等待,它被丢弃,因此立即开始下一次下 ...

8 在线程内使用Parallel.Foreach

我有一个线程在服务启动时加载数据。 我在此线程中调用一个使用Parallel.Foreach迭代一组数据的方法。 但是我在并行操作中具有的linq查询会获得未设置为实例错误的对象引用。 * 如果删除Parallel.Foreach或删除线程,则相同的逻辑起作用。 * 即使锁定列表也无 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM