Mr Heelis
Mr Heelis

Reputation: 2546

this BlockingCollection is not FIFO as I expected

thanks for looking at my question: I have a (non gui thread) BlockingCollection that I had always assumed would be FIFO (First in First Out) but I now realise it isn't

I have pasted an example of the code on dotnetfiddle but because it doesn't run multithreads you can't see the error happening, but you can see the code

ok. So what did I want? I wanted to make a second thread (non GUI) in Visual Studio Express 2013 C# Winforms that would be like a work house that would do the things it was passed in the order it was sent them.

I opted for a structure that is like this:

nofQDo
|_addAction(|)
            |
            +-> static BlockingCollection foreach
                                          |
                                          +-> QDo.run(|)
                                                      |
                                                      +> QDoType.action(//code//)

the reason for this strange arrangement was that I wanted to have up to 20 or 30 types of queue object (I call these all QDoType_something) and I am happy with the layout out but the engine doesn't work if I call

QDoType_test gra = new QDoType_test("hey0");
nofQDo.addAction(gra);

QDoType_test grb = new QDoType_test("hey1");
nofQDo.addAction(grb);

QDoType_test grc = new QDoType_test("hey2");
nofQDo.addAction(grc);

 QDoType_test grd = new QDoType_test("hey3");
 nofQDo.addAction(grd);

 QDoType_test gre = new QDoType_test("hey4");
 nofQDo.addAction(gre);

 QDoType_test grf = new QDoType_test("hey5");
 nofQDo.addAction(grf);

I get

00009::hey0
00009::hey1
00009::hey5
00009::hey3
00009::hey2
00009::hey4

or

00009::hey1
00009::hey0
00009::hey3
00009::hey2
00009::hey4
00009::hey5

so it clearly isn't "FIFO" and this is alarming.. is there a way to ensure that my BlockingCollection is a) not gui thread b) only ever running as one extra thread and c) that this second thread is always running FIFO (First in First Out?)

as requested: Here is the code proper:

=QDoType_test.cs=

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QTest
{
    class QDoType_test : QDoType
    {
        String szout = "";
        private string ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString("00000");
        public QDoType_test(String sent)
        {
            szout = sent;
        }
        public override void action()
        {
            System.Threading.Thread.Sleep(100);
            Console.WriteLine(ThreadId + "::" + szout);
        }
    }
}

=nofQDo.cs=

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace QTest
{
    class nofQDo
    {
        static BlockingCollection<QDo> queue = new BlockingCollection<QDo>(new ConcurrentQueue<QDo>()); //<--new ConcurrentQueue<QDo>() makes it FIFO

        public static void addAction(QDoType action)
        {
            QDo me = new QDo(action);
            queue.Add(me);
            Task.Factory.StartNew(() =>
            {
                foreach (QDo doThis in queue.GetConsumingEnumerable())
                {
                    doThis.run();
                }
            });
        }
    }
}

=QDoType.cs=

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QTest
{
    /// <summary>
    /// This is a Parent Class for QDoType_whatever they are non 
    /// communicative and most exist to run db calls
    /// </summary>
    public abstract class QDoType
    {
        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
         * this is a parent class not meant to ever be instaciated   *
        \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

        public string uniqueid = "";
        public Action callback;

        public abstract void action();

        /// <summary>
        /// kept for the fact you might want 
        /// to debug where it went in the Queue
        /// </summary>
        /// <param name="uid"></param>
        public void setUniqueId(string uid)
        {
            uniqueid = uid;
        }
    }
}

=QDo.cs=

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QTest
{
    class QDo
    {
        /***********
         * 
         * This class is the <T> umbrella for a real type that runs inside it
         * basically all this does in "run()" the QDoType;
         */
        public const bool DELETE_MODE = true;

        QDoType iam;

        public QDo(QDoType action)
        {
            DateTime dt = DateTime.Now;
            iam = action;
        }

        public void run()
        {
            iam.action();

            if (iam.callback != null) iam.callback();
        }
    }
}

Upvotes: 1

Views: 1412

Answers (1)

Mr Heelis
Mr Heelis

Reputation: 2546

ok I was nearly there it was merely making sure that the consumer started before the producer (i.e. in the constructor), still not 100% sure why this works (but it definitely does! I assure you 100%!) it is tested on single unit runs too.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace QTest
{
    class nofQDo
    {
        static BlockingCollection<QDo> queue = new BlockingCollection<QDo>(new ConcurrentQueue<QDo>()); //<--new ConcurrentQueue<QDo>() makes it FIFO

        static nofQDo()
        {
            Task.Factory.StartNew(() =>
            {
                foreach (QDo doThis in queue.GetConsumingEnumerable())
                {
                    doThis.run();
                }
            });
        }

        public static void addAction(QDoType action)
        {
            QDo me = new QDo(action);
            queue.Add(me);
        }
    }
}

so now

QDoType_test gra = new QDoType_test("hey0"); nofQDo.addAction(gra);

QDoType_test grb = new QDoType_test("hey1"); nofQDo.addAction(grb);

QDoType_test grc = new QDoType_test("hey2"); nofQDo.addAction(grc);

QDoType_test grd = new QDoType_test("hey3");  nofQDo.addAction(grd);

QDoType_test gre = new QDoType_test("hey4");  nofQDo.addAction(gre);

QDoType_test grf = new QDoType_test("hey5");  nofQDo.addAction(grf);

produces

00009::hey0
00009::hey1
00009::hey2
00009::hey3
00009::hey4
00009::hey5

Upvotes: 1

Related Questions