Search code examples
c#.netmultithreadingtask-parallel-librarytpl-dataflow

TPL Dataflow and console application doesn't terminate the application


I've a console application that should be called in a batch and compress huge files, I wanted to use DataFlow and everything works fine except for the completion

Consider please the following code

public static void CompressFiles(string folder)
{
    var bufferBlock = new BufferBlock<FileInfo>();

    bufferBlock.LinkTo(Target(), new DataflowLinkOptions() { PropagateCompletion = true});
        var files = GetFilesFromFolder(folder);

        Parallel.ForEach(files, file =>
        {
            bufferBlock.Post(file);
        });

        Stopwatch sw = new Stopwatch();

        sw.Start();
        Parallel.ForEach(files, file =>
        {
            CreateFileT(file.FullName);
        });

        sw.Stop();

       // Task.WaitAll(bufferBlock.Completion);

        bufferBlock.Completion.Wait();
        string tempoImpiegato =
            $"[Generatore PDZ] : tempo impiegato per la generazione di {files.Length} pdz : {sw.Elapsed}";

        LogManager.GetLogger("normal").Info(tempoImpiegato);
    }

and the compression ActionBlock that's

private static ActionBlock<FileInfo> Target()
    {
        return new ActionBlock<FileInfo>(file =>
        {
            FileInfo bolFileName = new FileInfo(file.FullName.Replace($"{ApplicationHelper.FileExtensionPdf}", $"{ApplicationHelper.FileExtensionBOL}"));
            FileInfo zipFileName = new FileInfo(file.FullName.Replace($"{ApplicationHelper.FileExtensionPdf}", $"{ApplicationHelper.FileExtensionPdz}"));

            if (!File.Exists(file.FullName) || !File.Exists(bolFileName.FullName))
            {
                string s = $"File {file.FullName} o {bolFileName.FullName} non esistenti o inaccessibili";
                Log.Warning(s);
            }
            else
            {
                try
                {
                    using (ZipOutputStream s = new ZipOutputStream(zipFileName.OpenWrite()))
                    {
                        s.SetLevel(9);

                        ZipEntry entry1 = new ZipEntry(file.Name);
                        ZipEntry entry2 = new ZipEntry(bolFileName.Name);

                        entry1.Size = file.Length;
                        entry2.Size = bolFileName.Length;

                        byte[] buffer = ReadBytesFromFileInfo(file);
                        byte[] bufferBol = ReadBytesFromFileInfo(bolFileName);

                        s.PutNextEntry(entry1);
                        s.Write(buffer,0,buffer.Length);
                        s.PutNextEntry(entry2);
                        s.Write(bufferBol, 0, bufferBol.Length);
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "");
                }
            }
        });
    }

Using bufferBlock.Completion.Wait() blocks the program without going further...what am I doing wrong since I've set PropagateCompletion to true and the Target() works (it generates the files)


Solution

  • You are waiting for completion but you are not signaling completion. The BufferBlock is waiting for more items, it doesn't know you aren't going to post any more items into it.

    To signal completion you need to call Complete before waiting for completion:

    block.Complete();
    await block.Completion;