【问题标题】:Missing rows from list when using dynamic task in dotnet在 dotnet 中使用动态任务时列表中缺少行
【发布时间】:2018-03-29 04:31:26
【问题描述】:

我是在 .Net 应用程序中使用 TPL 的新手。在创建一个简单的控制台应用程序来实现一些动态创建的并行任务时,我遇到了一些问题。

这里的问题是,当创建并运行 10 个任务时,虽然控制台显示了所有 10 个任务,但在将控制台和日志记录之间的延迟后写入日志文件时,日志文件会随机丢失一些项目.

下面是我的示例代码(这只是我实际代码的骨架)

class Program
{
    public static int datacount = 10;

    static void Main(string[] args)
    {
        List<Task> tasks = new List<Task>();
        var s1 = DateTime.Now;
        var transList = GenerateTransactionList();

        foreach (var transaction in transList)
        {
            Transactions transactionNew = new Transactions();
            transactionNew = transaction;
            tasks.Add(Task.Factory.StartNew(() => serialMethod(transactionNew)));

        }
        Task.WhenAll(tasks).Wait();
        Console.WriteLine("Completed!!!");
    }

    private static List<Transactions> GenerateTransactionList()
    {
        Random r = new Random();
        List<Transactions> transactionList = new List<Transactions>();
        for (int i = 1; i <= datacount; ++i)
        {
            Transactions tr = new Transactions();
            tr.ID = 0;
            tr.Amount = r.Next(1, 10);
            tr.Created_By = "Iteration" + i;
            tr.Notes = "Iteration" + i;
            tr.Created_On = DateTime.Now;
            transactionList.Add(tr);
        }
        return transactionList;
    }
    private static async Task<string> serialMethod(Transactions tlist)
    {
        Console.WriteLine("Started Serial Iteration" + tlist.Notes);
        try
        {
            Console.WriteLine("Finished Serial Iteration" + tlist.Notes);
            Thread.Sleep(10000);//doing some time consuming process
            WriteLog("Parallel2", DateTime.Now, DateTime.Now, tlist.Notes);

            return "Success";
        }
        catch (Exception ex)
        {
            Console.WriteLine("serialmethod" + ex.Message);
            return "Failure";
        }
    }

    public static void WriteLog(string type,
        DateTime startTime, DateTime endTime,
        string dataSet)
    {
        try
        {
            string logFolderPath = AppDomain.CurrentDomain.BaseDirectory + @"\Logs";

            if (!Directory.Exists(logFolderPath))
                Directory.CreateDirectory(logFolderPath);

            string logFilePath = logFolderPath + @"\Log_" + DateTime.Today.ToString("yyyy.MM.dd") + ".csv";

            string line = string.Empty;
            if (!File.Exists(logFilePath))
            {
                line = @"""Type"",""Start Time"",""End Time"",""Duration"",""Iteration""";
                writeLineToFile(logFilePath, line);
            }

            string duration = (endTime - startTime).ToString();

            line = "\"" + type + "\"," +
                   "\"" + startTime.ToString("MM/dd/yyyy hh:mm:ss tt") + "\"," +
                   "\"" + endTime.ToString("MM/dd/yyyy hh:mm:ss tt") + "\"," +
                    "\"" + duration + "\"," +
                   "\"" + dataSet + "\"";

            writeLineToFile(logFilePath, line);

        }
        catch (Exception)
        {
            //do nothing
        }
    }

    private static void writeLineToFile(string fileName, string line)
    {

        using (var writer = new StreamWriter(fileName, true))
        {
            writer.WriteLine(line);
        }
    }
}

class Transactions
{
    public int ID { get; set; }
    public decimal Amount { get; set; }
    public int Points { get; set; }
    public string Notes { get; set; }
    public string Created_By { get; set; }
    public DateTime Created_On { get; set; }

}

你知道为什么会这样吗?我尝试使用ConcurrentBag 而不是列表。但这也无济于事。如果我遗漏任何内容或我的实施完全错误,请指导并告诉我。

【问题讨论】:

  • 感谢@PeterBons 的信息。正如我在 Thread.Sleep(10000);// 执行一些耗时的过程中的示例代码所示,我将进行一些需要并行工作的多个数据库操作。那么,是否可以使用线程来完成它?如果是,我该怎么做?

标签: file logging concurrency task-parallel-library


【解决方案1】:

你的代码中有很多容易出错的行:

  1. 您正在覆盖 foreach 循环中的交易参考
  2. 您正在使用StartNew 方法而不是Tas.Run
  3. 您正在使用阻塞 WaitAll 而不是 await WhenAll,因此您会无缘无故地阻塞应用程序中的一个线程
  4. 您可以简单地切换到Parallel.Foreach 而不是foreach
  5. 最重要的是:您同时从不同的线程写入同一个文件,因此它们基本上是相互中断的。要么使用一些阻塞来写入文件(不能并行完成),要么使用一些库进行日志记录,比如NLog 或其他什么,所以它会为你处理日志记录
  6. 当其中一些线程尝试创建文件而其他线程已经创建文件时,您的线程可能会遇到这种情况,因此将文件的创建逻辑移到一个位置(NLog 等库将为您正确执行)李>
  7. 尝试使用对象初始化器而不是设置一个又一个属性:

    var tr = new Transactions
    {
        ID = 0,
        Amount = r.Next(1, 10),
        Created_By = "Iteration" + i,
        Notes = "Iteration" + i,
        Created_On = DateTime.Now
    }
    

【讨论】:

  • 感谢您的详细分析。实际上,我想要实现的是做一些并行数据库操作(例如:在对列表中的每个对象进行一些修改后,从列表中并行插入一些记录)。因此,在这样做的同时,许多记录都被遗漏了。尝试了任务和 foreach.parallel。在 foreach.parallel 中,从列表中再次处理相同的记录而不是不同的记录。有什么解决方案可以实现吗?
  • 在这种情况下,你肯定需要一个并发包。另外,请在您的问题中添加一些信息,现在很难说您的代码有什么问题
猜你喜欢
  • 1970-01-01
  • 2017-04-16
  • 2016-04-08
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 2019-08-18
相关资源
最近更新 更多