-7

I want to simulate producer Consumer in C#.One thread works as producer,which new array objects,and put that array in List.Another thread works as comsumer,when there are elements in the List,it get the array and do something. Codes are here.Why there are "null"s in the process. How to fix it? The problem mybe caused by when calling list.add(tmp), list.Count is added one before the ushort[] tmp is ready.Can anyone explain the mechanism of such process.

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

    namespace listTest
    {
        class Program
        {
            static List<ushort[]> list = new List<ushort[]>();
            static void func1()
            {
                while (true)
                {
                    ushort[] tmp;
                    while (true)
                    {
                        tmp = new ushort[100];
                        if (tmp != null)
                            break;
                    }


                    list.Add(tmp);
                    Thread.Sleep(10);
                }
            }
            static void func2()
            {
                while (true)
                {
                    if (list.Count > 0)
                    {
                        ushort[] data = list.ElementAt(0);
                        if (data == null)
                            Console.Write("null\n");
                        else
                        {
                            for (int i = 0; i < data.Length; i++)
                            {
                                data[i] += 1;
                            }
                        }
                        Console.Write("ok ");
                        list.RemoveAt(0);
                    }

                }
            }


            static void Main(string[] args)
            {
                Thread func1T = new Thread(func1);
                Thread func2T = new Thread(func2);
                func1T.Start();
                func2T.Start();
            }

        }
    }
sounder
  • 11
  • 2

1 Answers1

1

The problem why you sometimes get null is the concurrent access to your list with the operations Add and ElementAt. Since in your case ElementAt won't have to enumerate the list it will just check internally if the list is null and in the case it isn't it will return the element at the specified index like so list[index]. This is pretty lightweight so your problem lies in the source of the Add operation.

The Add operation first checks internally if its current capacity is big enough so that the new element can be added (it has more places reserved in memory than the amount of element it contains + 1). If it can be added it simply does so via the statement _items[_size++] = item;

In the case that not enough size was allocated previously the Add operation creates a new array with the new size which is needed. Afterwards the elements from the previously too small array need to be copied to the new array. This happens via the Array.Copy operation. Afterwards the internally held count of elements is incremented and then the element itself gets added.

As you can see there is pretty much going on behind the scenes. Personally I don't know where the problem really originates. My best guess is that the memory in the internal array is already allocated but the value isn't set while you access it with the ElementAt operation.

And such problems are why you need to manage the access to shared resources (resources which can be shared across threads). There are several techniques for this like locks, monitors, semaphores, ...

You could also change the type of the collection to a thread-safe collection which takes care of the synchronisation issues itself.

Manuel Zelenka
  • 1,616
  • 2
  • 12
  • 26