0

My clients run the same method which does something while(true) in the task? The method uses a static list. They take an element from list by your id, they never use the same element. And after it deletes this element from the list. I can have a lot of clients I don't know how much and I can't do my list for everyone. I cant send element to queue, because when one client lost connection would block other clients. Lock() doesn't work in Task.

This work for this moment, but throw exception.

Do you know any good way to solve my problem?

Task.Run(() =>
        {
            var element = list.First(x => x.Id == client.Id);
        }

Code from my Project:

 Task.Run(async() =>
        {
            await semaphore.WaitAsync();
            try
            {
                foreach (var s in dataToSend)
                {
                    data.Add(new DataForLists() { Id = s.Key, Model = s.Value });
                }
            }
            finally
            {
                semaphore.Release();
            }
        });

Second Method

        Task.Run(async () =>
        {
            while (true)
            {
                try
                {
                    if (Compute.data.Count > 0)
                    {

                        if (Compute.data.Any(x => x.Id == DictionaryStreamProvider.streamsDictTcpSend[stream]))
                        {
                            await Compute.semaphore.WaitAsync();
                            try
                            {
                                    var t = Compute.data.First(x => x.Id == DictionaryStreamProvider.streamsDictTcpSend[stream]);
                                    Compute.data.Remove(t);
                                    formatter.Serialize(stream, t.Model);
                                    Compute.data.Remove(t);
                                    Console.WriteLine("Wyslano {0} na liste: {1}", t.Model.Count, data2);                                    
                            }
                            finally
                            {
                                Compute.semaphore.Release();
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        });
  • What exception? – TimChang Sep 26 '19 at 10:10
  • Post the exception. – Phat Huynh Sep 26 '19 at 10:10
  • System.InvalidOperationException: Collection was modified: enumeration operation may not execute. – mdlejtecole Sep 26 '19 at 10:13
  • Possible duplicate of [Collection was modified; enumeration operation may not execute](https://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute) – SᴇM Sep 26 '19 at 10:14
  • update delete a list isnot thread safe, use lock (this work in task, please share your code) or System.Collections.Concurrent – LeBigCat Sep 26 '19 at 10:15
  • I think this is different problem. i used lock, and this nothing give me. I cant share code. I sometimes add element to list. in task i have while(true) in while i have if(). When i have element in list for id it take it, send and remove. – mdlejtecole Sep 26 '19 at 10:24

1 Answers1

2

If you change a List (add or remove an element) while someone else is enumerating it, when that other consumer requests the next item, they will get this collection. Internally, List keeps a version number of the collection which updates whenever you modify it. This invalidates any existing enumerators on the collection.

You may solve this issue by either locking the collection in some way (using a SemaphoreSlim for example), or by using one of the Thread-safe collections.

Daniel Crha
  • 675
  • 5
  • 13
  • i have something like this and i can only block "if", or it what is in "if". Will semapgoreSlim work for me? Task.Run(() => { whlie(true) { if(1==id) { var element = list.First(x => x.Id == client.Id); } } } – mdlejtecole Sep 26 '19 at 10:28
  • You would create a `new SemaphoreSlim(1,1)`, and in every place where you want to access the List (*both reading and writing*), you would `var enteredSuccessfully = await semaphore.WaitAsync()`. You would check if `enteredSuccessfully == true`, and if yes, then do your work with the list wrapped in a `try` block and then in a `finally` block, you would call `semaphore.Release()`. – Daniel Crha Sep 26 '19 at 10:37
  • var enteredSuccessfully = await EkLokDataSeparator.semaphore.WaitAsync(); causes an error. The await operator can only be used in async lambda expression. When i add async to Task it throw "Cannot assign void to an implicitly-typed veriable. – mdlejtecole Sep 26 '19 at 11:49
  • Ah my bad, I was thinking of one of the other overloads of `WaitAsync`. With this one, it has no return value, so just `await semaphore.WaitAsync()` and you can skip the `enteredSuccessfully == true` check. – Daniel Crha Sep 26 '19 at 11:52
  • try { await Compute.semaphore.WaitAsync(); //Do something } finally { Compute.semaphore.Release(); } It dosnt work. Still throw exception – mdlejtecole Sep 26 '19 at 12:03
  • What exception are you getting? Also the `WaitAsync` await should be in front of the try block, not inside it. If the WaitAsync call fails, it would attempt to release the semaphore when it shouldn't. – Daniel Crha Sep 26 '19 at 12:13
  • Still dont work ;/ System.InvalidOperationException: Collection was modified: enumeration operation may not execute – mdlejtecole Sep 26 '19 at 12:18
  • You should use this pattern with the semaphore in *every* place where you interact with the collection. Every place where you are adding, removing an item from the list or reading from the list, should be surrounded by the semaphore section. – Daniel Crha Sep 26 '19 at 12:20
  • I only do that i 2 place and i did it there. – mdlejtecole Sep 26 '19 at 12:21
  • Unless you provide a code sample then I don't think I can help you much more, since you are clearly not using the semaphore somewhere where you should. Also be careful that any LINQ queries which return IEnumerable may be lazily evaluated, which means you should call .ToList() if you are using this result outside the critical (locked) section. – Daniel Crha Sep 26 '19 at 12:25
  • Thanks for trying help me, i Add code from my project. This task are in method. Where each of these methods are Run from other task. They work whole time. With ostepy ~1sec – mdlejtecole Sep 26 '19 at 12:33
  • You have some access to the list outside the critical section. Namely the two ifs (`Compute.data.Count > 0` and `Compute.data.Any(x => x.Id == DictionaryStreamProvider.streamsDictTcpSend[stream])` should both also be *after* the `semaphore.WaitAsync()`. That is the issue. – Daniel Crha Sep 26 '19 at 12:39
  • Unfortunately when i did it the program have stack in one place, for this moment i dont know where exactly. Because data overlap and this make throw more exception and dont let me well debug. But probably in second method. – mdlejtecole Sep 26 '19 at 12:59
  • It stack in 1 place i put braek point in finally. I was 15/20 times in this finally, And after it nothing more. not once enter to second method – mdlejtecole Sep 26 '19 at 13:13
  • Would it be possible to put a minimal example of this problem on dotnetfiddle or some similar site where you can run the code snippet? – Daniel Crha Sep 26 '19 at 13:46