0
votes

I am populating a List A-Sync and I want to show the last 20 objects in a console. This is causing me a huge pain in the ass because I'm getting exceptions like: "Collection was modified after the enumerator was instantiated." and "Collection was modified after the enumerator was instantiated."

Right now this is what I'm trying (note that this is pseudo code):

Initiate the a-sync task which will fill the list

stopWatch.Start();

program.doAsynctask(InputList, ref OutputList, sniffNotify);

while (program.busy)
{
     print();
     Thread.Sleep(200);
}

stopWatch.Stop();

Console.Clear();
Console.WriteLine("Task done!");

The method that does the task initiation

public void doAsynctask(List<Input> InputList, ref List<Result> OutputList, Action<Result> callback)
{
    if (busy)
        return;

    if (Input== null || Input.Count() == 0)
    {
        return;
    }

    busy = true;
    taskCount = Input.Count();
    this.InputList = InputList;
    this.OutputList = OutputList;

    //initiate
    Task.Run(() => innerAsyncTask(callback));

}

The method that does the task

private void innerAsyncTask(Action<Result> callback)
{

    foreach (Input inputObject in InputList)
    {
        currentInputObject = inputObject;
        Result result = new Result();

        result = doWebRequestOrsomethingThatTakesTime(inputObject);
        Thread.Sleep(250); //simulate some latency

        OutputList.Add(result);

        //notify
        callback(result);

        currentTask++;

    }

    busy = false;
}

This method is called by the callback

public void sniffNotify(Result sr)
{
    printQueue.Add(sr); //printQueue is a queue that can only contain 20 items
}

This is the print function where I get the exception (I tried to use a lock but is does not help)

public void print()
{
    TimeSpan ts = stopWatch.Elapsed;
    string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
    ts.Hours, ts.Minutes, ts.Seconds,
    ts.Milliseconds / 10);

    Console.Clear();
    Console.WriteLine("Running program");
    Console.WriteLine("");
    Console.WriteLine("Time elapsed: "+ elapsedTime);
    Console.WriteLine(String.Format("{0} of the {1} tasks are done", program.currentTask, program.taskCount));
    Console.WriteLine((program.busy ? String.Format("Bussy with {0}", program.currentInputObject) : "All done"));

    Console.WriteLine("");
    Console.WriteLine("");

    lock (printQueue) {
        foreach (Result res in printQueue) { //Getting "Collection was modified after the enumerator was instantiated." exception here
            Console.WriteLine(String.Format("Done result {0}: \t\t {1}", res.id, res.value));
        }
    }

}

This is the printQueue Class

class FixedSizeList<T> : IEnumerable where T : new()
{
    public int size { get; set; }
    private Queue<T> items = new Queue<T>();

    public int Count { get{ return items.Count(); } }

    public FixedSizeList(int size)
    {
        this.size = size;

        for (int a = 0; a < size; a++)
        {
            items.Enqueue(new T());
        }
    }

    public void Add(T item)
    {
        items.Enqueue(item);
        if (items.Count >= size)
        {
            items.Dequeue();
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    internal List<T> ToList()
    {
        return items.ToList();
    }
}

Now I have the idea that I am completely doing this the wrong way. How can I achieve what I want?

2

2 Answers

2
votes

You can't modifiy (add in your case) items in printQueue whilst it is being enumerated. Try taking a copy of it first:

foreach (Result res in printQueue.ToList())
{
    ...
}

However it seems like your tasks haven't finished running so you might want to address that first.

1
votes

Use an actual Queue

    public Queue<Result> printQueue = new Queue<Result>();

so that you can enqueue

    public static void sniffNotify(Result sr)
    {
        program.printQueue.Enqueue(sr); //printQueue is a queue that can only contain 20 items
    }

and dequeue

            while (program.printQueue.Count>0)
            {
                Result res = program.printQueue.Dequeue();
                Console.WriteLine(String.Format("Done result {0}: \t\t {1}", res.id, res.value));
            }