简体   繁体   中英

Saving images via producer-consumer pattern using BlockingCollection

I'm facing a producer-consumer problem: I have a camera that sends images very quickly and I have to save them to disk. The images are in the form of ushort[] . The camera always overrides the same variable of type ushort[] . So between one acquisition and another I have to copy the array and when possible, save it in order to free up the memory of that image. The important thing is not to lose any images from the camera, even if it means increasing the memory used: it is entirely acceptable that the consumer (saving images with freeing of memory) is slower than the producer; however, it is not acceptable not to copy the image into memory in time .

I've written sample code that should simulate the problem:

  • immage_ushort : is the image generated by the camera that must be copied to the BlockingCollection before the next image arrives
  • producerTask : has a cycle that should simulate the arrival of the image every time_wait; within this time the producer should copy the image in the BlockingCollection.
  • consumerTask : must work on the BlockingCollection by saving the images to disk and thus freeing up the memory; it doesn't matter if the consumer works slower than the producer.

I put a time_wait of 1 millisecond, to test the performance (actually the camera will not be able to reach that speed). The times are respected (with a maximum delay of 1-2 ms, therefore acceptable) if there is no saving to disk in the code (commenting image1.ImWrite (file_name) ). But with saving to disk on, I instead get delays that sometimes exceed 100ms.

This is my code:

private void Execute_test_producer_consumer1()
{
    //Images are stored as ushort array, so we create a BlockingCollection<ushort[]>
    //to keep images when they arrive from camera
    BlockingCollection<ushort[]> imglist = new BlockingCollection<ushort[]>();

    string lod_date = "";

    /*producerTask simulates a camera that returns an image every time_wait
    milliseconds. The image is copied and inserted in the BlockingCollection 
    to be then saved on disk in the consumerTask*/
    Task producerTask = Task.Factory.StartNew(() =>
    {
        //Number of images to process
        int num_img = 3000;
        //Time between one image and the next
        long time_wait = 1;

        //Time log variables
        var watch1 = System.Diagnostics.Stopwatch.StartNew();
        long watch_log = 0;
        long delta_time = 0;
        long timer1 = 0;
        List<long> timer_delta_log = new List<long>();
        List<long> timer_delta_log_time = new List<long>();

        int ii = 0;

        Console.WriteLine("-----START producer");

        watch1.Restart();

        //Here I expect every wait_time (or a little more) an image will be inserted
        //into imglist

        while (ii < num_img)
        {
            timer1 = watch1.ElapsedMilliseconds;
            delta_time = timer1 - watch_log;

            if (delta_time >= time_wait || ii == 0)
            {
                //Add image
                imglist.Add((ushort[])immage_ushort.Clone());

                //Inserting data for time log
                timer_delta_log.Add(delta_time);
                timer_delta_log_time.Add(timer1);
                watch_log = timer1;
                ii++;
            }
        }

        imglist.CompleteAdding();
        watch1.Stop();

        lod_date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        Console.WriteLine("-----END producer: " + lod_date);

        // We only print images that are not inserted on schedule

        int gg = 0;
        foreach (long timer_delta_log_t in timer_delta_log)
        {
            if (timer_delta_log_t > time_wait)
            {
                Console.WriteLine("-- Image " + (gg + 1) + ", delta: "
                    + timer_delta_log_t + ", time: " + timer_delta_log_time[gg]);
            }
            gg++;
        }
    });

    Task consumerTask = Task.Factory.StartNew(() =>
    {
        string file_name = "";
        int yy = 0;

        // saving images and removing data
        foreach (ushort[] imm in imglist.GetConsumingEnumerable())
        {
            file_name = @"output/" + yy + ".png";
            Mat image1 = new Mat(row, col, MatType.CV_16UC1, imm);

            //By commenting on this line, the timing of the producer is respected
            image1.ImWrite(file_name);
            image1.Dispose();
            yy++;
        }

        imglist.Dispose();

        lod_date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        Console.WriteLine("-----END consumer: " + lod_date);
    });
}

I thought, also, that the BlockingCollection could remain blocked for the entire duration of the foreach and therefore of saving the image to disk. So I also tried replacing the foreach with this:

while(!imglist.IsCompleted)
{
    ushort[] elem = imglist.Take();
    file_name = @"output/" + yy + ".png";
    Mat image1 = new Mat(row, col, MatType.CV_16UC1, elem);

    //By commenting on this line, the timing of the producer is respected
    image1.ImWrite(file_name);
    image1.Dispose();
    yy++;
}

But the result doesn't change.

What am I doing wrong?

You migth want to start your tasks with the "LongRunning" option :

LongRunning Specifies that a task will be a long-running, coarse-grained operation involving fewer, larger components than fine-grained systems. It provides a hint to the TaskScheduler that oversubscription may be warranted. Oversubscription lets you create more threads than the available number of hardware threads. It also provides a hint to the task scheduler that an additional thread might be required for the task so that it does not block the forward progress of other threads or work items on the local thread-pool queue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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