C # Я перебираю очередь задач и получаю null, почему это так?

#c# #loops #queue #task

#c# #циклы #очередь #задача

Вопрос:

Привет, stackoverflow!

Мне нужно обработать, например, две задачи в цикле While, одна из них добавляет строку в свой список, а другая читает список, каждый из них регистрируется в очереди (B = пользовательский класс с ключом и задачей в нем)

вчера я привел пример этого, но я получил странную проблему, некоторые из моих задач были «нулевыми», и я не уверен, почему ?! Самое смешное, что его задача ~ 180 не одна из первых..

Вот мой код:

 class Program
{
    static HandlerMessages m;
    static int i = 0;
    static void Main(string[] args)
    {
        HandlerQueue.Run();
        m = new HandlerMessages();
        Console.WriteLine("fe");
        Task.Run(() =>
        {
            while (true)
            {
                m.Add((i  ).ToString());
                System.Threading.Thread.Sleep(500);
            }
        });

        Task.Run(() =>
        {
            while (true)
            {
                m.Read((i  ).ToString());
                System.Threading.Thread.Sleep(200);
            }
        });

        Console.ReadLine();
    }
}

public class B
{
    public Task task;
    public string key;
}

public class HandlerMessages
{
    List<string> ll = new List<string>();
    public void Add(string str)
    {
        var action = new Action(() => { ll.Add($"{str}"); });
        var task = new Task(action);
        var b = new B();
        b.key = "Add: "   str;
        b.task = task;
        HandlerQueue.AddTo(b);
    }

    public void Read(string str)
    {
        var action = new Action(() =>
        {
            foreach (var item in ll)
            {
            }
        });
        var task = new Task(action);
        var b = new B();
        b.key = "Read: "   str;
        b.task = task;
        HandlerQueue.AddTo(b);
    }
}

public class HandlerQueue
{
    public static Queue<B> queue = new Queue<B>();
    public static void AddTo(B o)
    {
        queue.Enqueue(o);
    }

    public static void Run()
    {
        Task.Run(() =>
        {
            while (true)
            {
                if (queue.Count != 0)
                {
                    var task = queue.Dequeue();
                    if (task != null)
                    {
                        Console.WriteLine(task.key);
                        task.task.Start();
                        task.task.Wait();
                    }
                    else
                    {
                        Console.WriteLine("Error-------");
                    }
                }
            }
        });
    }
}
 

Комментарии:

1. Это не является потокобезопасным ни при каком воображении…

2. Вот почему я пытался сделать это в очереди, когда я пытаюсь использовать lock(object), я получаю ту же ошибку…

3. Что произойдет, когда задача равна null? исключение или консоль. Строка записи без значения?

4. Да, вообще никакого значения и исключение Nullpointer , но проблема в том, что у этой задачи есть номер, и это число представляет мой «тестовый код», а когда число равно нулю, то и мой «тестовый код» тоже .. i.imgur.com/vYuV8wV.png

5. Проблема с этим кодом заключается в его беспорядке, есть циклы, которые ничего не делают, делегируют без причины. Вы создаете задачи с помощью конструктора (что всегда плохо), даже когда он уже вызывается в задаче, и многие виды использования общих ресурсов в небезопасном поместье. Трудно понять, с чего начать, есть много проблем.

Ответ №1:

Изменить

 public static Queue<B> queue = new Queue<B>();
...
...
var task = queue.Dequeue();
 

Для

 public static ConcurrentQueue<B> queue = new ConcurrentQueue<B>();
...
...
queue.TryDequeue(out B task);

 

В соответствии с классом очереди Часть безопасности потоков

Безопасность потоков Общедоступные статические (общие в Visual Basic) члены этого типа являются потокобезопасными. Не гарантируется потокобезопасность любых членов экземпляра.

Чтобы гарантировать потокобезопасность очереди, все операции должны выполняться через оболочку, возвращаемую методом Synchronized(Queue) .

Перечисление через коллекцию по сути не является потокобезопасной процедурой. Даже когда коллекция синхронизирована, другие потоки все равно могут изменять коллекцию, что приводит к тому, что перечислитель выдает исключение. Чтобы гарантировать потокобезопасность во время перечисления, вы можете либо заблокировать коллекцию во время всего перечисления, либо перехватить исключения, возникающие в результате изменений, внесенных другими потоками.

Класс ConcurrentQueue

Согласно ConcurrentQueue .Метод TryDequeue(T)

Вам нужно обрабатывать то, что вы хотите, когда результат равен false

Замечания ConcurrentQueue обрабатывает всю синхронизацию внутри. Если два потока вызывают TryDequeue в один и тот же момент, ни одна операция не блокируется. Когда обнаруживается конфликт между двумя потоками, один поток должен повторить попытку для извлечения следующего элемента, и синхронизация обрабатывается внутренне.

Ответ №2:

Да, я думаю, что решение было «потокобезопасным»..

Я просто изменил его на:

 private static object locki = false;
public static void AddTo(B o)
    {
        lock (locki)
        {
            queue.Enqueue(o);
        }
    }


    public static void Run()
    {
        Task.Run(() =>
        {
            while (true)
            {
                if (queue.Count != 0)
                {
                    lock (locki)
                    {
                        var task = queue.Dequeue();
                        if (task != null)
                        {
                            Console.WriteLine(task.key);
                            task.task.Start();
                            task.task.Wait();
                        }
                        else
                        {
                            Console.WriteLine("Error----------------------------------------------------------------------------");
                        }
                    }
                }
            }
        });
    }
 

Теперь не получаю никаких ошибок 🙂 Спасибо, ребята.