Нужна помощь с доп. потоком, вернее с синхронизацией
От: mcka  
Дата: 14.10.13 06:50
Оценка:
Всем привет, уже третий день не могу разобраться с доп. потоком, вернее с синхронизацией. Нужна помощь, т.к. я только начал программировать на C# в VS2012.
Задача:
На форме две кнопки — "Start thread" и "Stop thread" и поле — richTextBox.
При нажатии на кнопку "Start thread" останавливается поток, если он был создан, иначе создается доп. поток в котором выводится инфа в richTextBox.
При нажатии на кнопку "Stop thread" поток должен корректно остановится, т.е. не убить поток, а в экстренном случаи должен завершить свою работу.
При закрытии приложении, если поток в работе необходимо опять же корректно остановить его.
Казалось все просто, но я столкнулся с проблемой: программа виснет.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication10
{
    delegate void SetTextCallback(string text);

    public partial class Form1 : Form
    {
        Thread th;

        public Form1()
        {
            InitializeComponent();
        }

        private void SetText(string text)
        {
            richTextBox1.AppendText(text + "\n");
        }

        public void SetTextSafe(string value)
        {
            if (richTextBox1.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                Invoke(d, new object[] { value + " (Invoke)" });
            }
            else
            {
                // It's on the same thread, no need for Invoke
                SetText(value + " (No Invoke)");
            }
        }
        public void StopThread()
        {

            if (th != null)
            {
                th.Interrupt();

                th.Join(); // Здесь зависон !!!
 
                // если зделать, так:
                //while (th.Join(10) == false) { Application.DoEvents(); }
                // то казалось бы все решено, но из-за DoEvents() возникают другие проблеммы, такие как повторный вход в тот же метод StopThread()

                SetTextSafe("OK...");
                SetText("");
                th = null;
            }
        }

        public void StartThread()
        {
            StopThread();

            th = new Thread(new ThreadStart(this.ThreadProcSafe));
            SetTextSafe("GO...");
            th.Start();
        }

        private void ThreadProcSafe()
        {
            SetTextSafe("Start...");
            try
            {
                int i = 0;
                while (i < 1000)
                {
                    SetTextSafe(i.ToString());

                    i++;
                    Thread.Sleep(100);
                }
            }
            catch (ThreadInterruptedException)
            {
                SetTextSafe("terminate");
            }
            
            SetTextSafe("stop");
        }

        private void Safebutton_Click(object sender, EventArgs e)
        {
            StartThread();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            StopThread();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            StopThread();
        }
    }
}


Результат должен быть такой:
GO... (No Invoke)
Start... (Invoke)
0 (Invoke)
1 (Invoke)
2 (Invoke)
3 (Invoke)
4 (Invoke)
terminate (Invoke)
stop (Invoke)
OK... (No Invoke)

GO... (No Invoke)
Start... (Invoke)
0 (Invoke)
1 (Invoke)
2 (Invoke)
3 (Invoke)
4 (Invoke)
terminate (Invoke)
stop (Invoke)
OK... (No Invoke)


Как я понял зависон из-за th.Join();, т.к. он блокирует вызывающий поток до завершения доп. потока. Но при этом доп. поток хочет синхронно вывести инфу в richTextBox — "terminate" и "stop", а основной поток приостановлен (блокирован).
Пытался c AutoResetEvent autoEvent вместо th.Join(); использовал autoEvent.WaitOne(), результат тот же — зависон на autoEvent.WaitOne()
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.