none
Вопрос по Потокам в C# RRS feed

  • Вопрос

  • Запускаю несколько потоков, которые будут выполнятся по очереди.

    Реализую это блокировками.

    System.Threading.Monitor.Enter(this);

    тут код.

    System.Threading.Monitor.Exit(this);

    Далее к примеру при нажатии кнопки потоки должны останавливаться, я их убиваю и удаляю.

    Далее я хочу снова создать и запустить потоки, НО когда я останавливал и удалял потоки ( блокировка System.Threading.Monitor.Enter(this); видимо не отыграла).

    Т.е я создаю новые потоки и запускаю их, но они не будут работать, т.к стоит блокировка.. 

    Как можно решить эту проблему? Т.е не из самого потока грамотно завершить все потоки и самое главное снять снять блокировку.

    Заранее спасибо!

    1 октября 2013 г. 9:48

Ответы

  • Небольшой вариант:

        class Program
        {
            private static void Main(string[] args)
            {
                Start();
                Console.ReadKey();
            }
    
            private static void Start()
            {
                var ids = new[] { 0, 1, 2, 3, 4, 5 };
                // Создаем токен для отмены
                var cancellationTokenSource = new CancellationTokenSource();
                foreach (var id in ids)
                {
                    // Запускаем задачи и передаем объект-состояние и токен отмены
                    var value = id;
                    Task.Factory.StartNew(() => Read(value, cancellationTokenSource.Token), cancellationTokenSource.Token);
                }
                // Отменяем все задачи
                Task.Factory.StartNew(() => Close(cancellationTokenSource), cancellationTokenSource.Token);
                // Или же просто так
                // Close(cancellationTokenSource);
            }
    
            //здесь необходимо завершить потоки.
            private static void Close(CancellationTokenSource cancellationTokenSource)
            {
                Thread.Sleep(5000);
                // Отмена все потоков, связанных с этим токеном
                cancellationTokenSource.Cancel();
            }
    
            //Метод для потоков.
            private static void Read(Object id, CancellationToken tokenSource)
            {
                Console.WriteLine("Thread {0}", id);
                while (true)
                {
                    // Прверяем, была ли вызвана отмена
                    if (tokenSource.IsCancellationRequested)
                    {
                        // Завершаем выполнение
                        Console.WriteLine("Cancel thread {0}", id);
                        break;
                    }
                }
            }
        }

    В данном случае для всех потоков существует один и тот же токен отмены. Соответственно, если происходит отмена - об этом узнают все потоки. Однако для большой гибкости можно для каждой задачи создавать свой токен, что позволит отменять только требуемые потоки (а не все сразу).

    • Помечено в качестве ответа ivanich274 4 октября 2013 г. 4:43
    2 октября 2013 г. 7:44

Все ответы

  • А как Вы убиваете потоки? Можно вызвать Abort или Interrupt для потока, а в функции потока перехватывать исключение ThreadAbortException или ThreadInterruptedException соответственно и выполнять в обработчике все действия по снятию блокировок и проч.
    • Предложено в качестве ответа kosuke904 1 октября 2013 г. 11:07
    1 октября 2013 г. 11:05
  • Еще вариант: использовать в функции потока конструкцию C# lock. Она как раз и реализована при помощи монитора, только Enter и Exit вызываются автоматически.

    lock(this)
    {
        // здесь код
    }

    1 октября 2013 г. 11:16
  • Вообще удаление/завершение потоков не рекомендуется делать. Уж лучше отмену через CancellationTokenSource.

    1 октября 2013 г. 19:21
  • Весь метод брать в try {} catch{} наверно будет не оправдано с точки зрения оптимизации.
    2 октября 2013 г. 5:28
  • Вариант интересный, но у меня в методе для потоков есть разветвления, и при определённых условиях поток освобождается в разных местах.
    2 октября 2013 г. 5:30
  • Возможно это то, что мне нужно, не могли бы вы написать пример?
    Я конечно читаю сейчас про этот класс, но пока не до конца понял как он работает.


    • Помечено в качестве ответа ivanich274 2 октября 2013 г. 5:33
    • Снята пометка об ответе ivanich274 2 октября 2013 г. 5:33
    2 октября 2013 г. 5:31
  • Если Вы приведете пример своего кода, то можно попробовать сразу переделать его с помощью отмены.
    2 октября 2013 г. 6:42
  • //Метод для потоков.
    void Read(object TestList)
    {
    do
    {
    
    System.Threading.Monitor.Enter(this);
    
    // тут основной алгоритм программы.
    
     
    System.Threading.Monitor.Exit(this);
     
    Thread.Sleep(100000);
    
    }
    while(true);
    
    
    }
    
    //создание и запуск потоков.
    private void start_click(object sender, EventArgs e)
    {
    while(..)
    {
    Thread OutRead = new Thread(Read);
    OutRead.Name = Arrays[index].ID.ToString();
    OutReadLIST.Add(OutRead);
    OutRead.Start(Arrays[index]);
    }
    }
    
    //здесь необходимо завершить потоки и освободить блокировки.
    
    private void close(object sender, EventArgs e)
    { 
    int index = 0;
    while (index != OutReadLIST.Count())
    {
    OutReadLIST[index].Abort();                
    index++;
    }
    OutReadLIST.Clear();
    
    }
    


    • Изменено ivanich274 2 октября 2013 г. 8:55
    2 октября 2013 г. 6:55
  • Вот так нужно оформить метод для потока:

    //Метод для потоков.
    void Read(object TestList)
    {
      do
      {
    
        lock(this)
        {
           // тут основной алгоритм программы.
        }
    
        Thread.Sleep(100000);
    
      }
      while(true);
    }
    
    Больше никаких изменений не потребуется, т.к. блокировка будет гарантировано освобождена в случае исключения.

    2 октября 2013 г. 7:31
  • Небольшой вариант:

        class Program
        {
            private static void Main(string[] args)
            {
                Start();
                Console.ReadKey();
            }
    
            private static void Start()
            {
                var ids = new[] { 0, 1, 2, 3, 4, 5 };
                // Создаем токен для отмены
                var cancellationTokenSource = new CancellationTokenSource();
                foreach (var id in ids)
                {
                    // Запускаем задачи и передаем объект-состояние и токен отмены
                    var value = id;
                    Task.Factory.StartNew(() => Read(value, cancellationTokenSource.Token), cancellationTokenSource.Token);
                }
                // Отменяем все задачи
                Task.Factory.StartNew(() => Close(cancellationTokenSource), cancellationTokenSource.Token);
                // Или же просто так
                // Close(cancellationTokenSource);
            }
    
            //здесь необходимо завершить потоки.
            private static void Close(CancellationTokenSource cancellationTokenSource)
            {
                Thread.Sleep(5000);
                // Отмена все потоков, связанных с этим токеном
                cancellationTokenSource.Cancel();
            }
    
            //Метод для потоков.
            private static void Read(Object id, CancellationToken tokenSource)
            {
                Console.WriteLine("Thread {0}", id);
                while (true)
                {
                    // Прверяем, была ли вызвана отмена
                    if (tokenSource.IsCancellationRequested)
                    {
                        // Завершаем выполнение
                        Console.WriteLine("Cancel thread {0}", id);
                        break;
                    }
                }
            }
        }

    В данном случае для всех потоков существует один и тот же токен отмены. Соответственно, если происходит отмена - об этом узнают все потоки. Однако для большой гибкости можно для каждой задачи создавать свой токен, что позволит отменять только требуемые потоки (а не все сразу).

    • Помечено в качестве ответа ivanich274 4 октября 2013 г. 4:43
    2 октября 2013 г. 7:44
  • Так тоже можно, но только изменить lock(THIS)!!!

        class Program
        {
            static readonly Object sync = new Object();
    
            private static void Main(string[] args)
            {
                Start();
                Console.ReadKey();
            }
    
            private static void Start()
            {
                var ids = new[] { 0, 1, 2, 3, 4, 5 };
                var threads = new Collection<Thread>();
                foreach (var id in ids)
                {
                    var thread = new Thread(Read) {Name = String.Format("Thread {0}", id)};
                    threads.Add(thread);
                    thread.Start(id);
                }
    
                Thread.Sleep(1000);
                threads.ElementAt(0).Abort();
            }
    
            //Метод для потоков.
            static void Read(object id)
            {
                Console.WriteLine("Thread {0}", id);
                do
                {
    
                    lock (sync)
                    {
                        Console.WriteLine("Lock thread {0}", id);
                        // тут основной алгоритм программы.
                        Thread.Sleep(10000);
                    }
                }
                while (true);
            }
        }
    • Помечено в качестве ответа ivanich274 2 октября 2013 г. 10:27
    • Снята пометка об ответе ivanich274 2 октября 2013 г. 10:31
    • Предложено в качестве ответа Medet Tleukabiluly 2 октября 2013 г. 19:16
    2 октября 2013 г. 8:10
  • Безусловно, но только с точки зрения добавления новых критических секций в будущем. В данном случае это не имеет никакого отношения к вопросу уважаемого ivanich274.
    2 октября 2013 г. 8:18
  • Небольшой вариант:

        class Program
        {
            private static void Main(string[] args)
            {
                Start();
                Console.ReadKey();
            }
    
            private static void Start()
            {
                var ids = new[] { 0, 1, 2, 3, 4, 5 };
                // Создаем токен для отмены
                var cancellationTokenSource = new CancellationTokenSource();
                foreach (var id in ids)
                {
                    // Запускаем задачи и передаем объект-состояние и токен отмены
                    var value = id;
                    Task.Factory.StartNew(() => Read(value, cancellationTokenSource.Token), cancellationTokenSource.Token);
                }
                // Отменяем все задачи
                Task.Factory.StartNew(() => Close(cancellationTokenSource), cancellationTokenSource.Token);
                // Или же просто так
                // Close(cancellationTokenSource);
            }
    
            //здесь необходимо завершить потоки.
            private static void Close(CancellationTokenSource cancellationTokenSource)
            {
                Thread.Sleep(5000);
                // Отмена все потоков, связанных с этим токеном
                cancellationTokenSource.Cancel();
            }
    
            //Метод для потоков.
            private static void Read(Object id, CancellationToken tokenSource)
            {
                Console.WriteLine("Thread {0}", id);
                while (true)
                {
                    // Прверяем, была ли вызвана отмена
                    if (tokenSource.IsCancellationRequested)
                    {
                        // Завершаем выполнение
                        Console.WriteLine("Cancel thread {0}", id);
                        break;
                    }
                }
            }
        }

    В данном случае для всех потоков существует один и тот же токен отмены. Соответственно, если происходит отмена - об этом узнают все потоки. Однако для большой гибкости можно для каждой задачи создавать свой токен, что позволит отменять только требуемые потоки (а не все сразу).

    Отлично, но где здесь блокировка потоков?И куда вообще поместить алгоритм?

    do
    {
       Monitor.Enter
    
      //код  алгоритма
       Monitror.Exit;
    
    }
    while(true);
    Можно пожалуйста на примере моего кода?


     Заранее спасибо.

    2 октября 2013 г. 9:10
  • В метод Read и помещаете свой алгоритм.
    2 октября 2013 г. 11:56
  • В метод Read и помещаете свой алгоритм.
    Это понятно,  не понятно куда именно. 
    2 октября 2013 г. 12:09
  • do
    {
       Monitor.Enter
    
      //код  алгоритма
       Monitror.Exit;
    
    }
    while(true);

    Помещаете в метод Read, и там где надо делаете проверку отмены выполнения потока

    if (tokenSource.IsCancellationRequested)

    2 октября 2013 г. 14:25
  • do
    {
       Monitor.Enter
    
      //код  алгоритма
       Monitror.Exit;
    
    }
    while(true);

    Помещаете в метод Read, и там где надо делаете проверку отмены выполнения потока

    if (tokenSource.IsCancellationRequested)


    В том и дело, что я не знаю в каком именно месте кода я захочу остановить поток, я же не буду через каждую строчку писать условие

    if (tokenSource.IsCancellationRequested)
    3 октября 2013 г. 9:09
  • Здравствуйте. Механизм, предложенный уважаемым Kirill Bessonov, это попытка Microsoft реализовать в .NET Framework версии 4 и выше концепцию 'Cancellation points", которая давно существовала в Unix-подобных системах. Смысл ее - явным образом задать точки в коде потока, в которых поток можно безболезненно прервать. Эти точки, как я понимаю, теперь присутствуют во многих функциях библиотеки .NET Framework. Если Вы хотите задействовать этот механизм в своем коде, то самостоятельно должны принять решение о том, где разместить такие точки прерывания. А вообще, мне кажется, что данная дискуссия уже выходит за рамки Вашего вопроса. Вам было предложено ДВА решения. Определитесь, какое из них подходит Вам и закрывайте вопрос.
    3 октября 2013 г. 9:32