2013-09-06 6 views
0

Предположим, у меня есть одна форма с именем mainFrm и объект, называемый objectA, который содержит текущие потоки. Когда произойдет какое-либо из этих двух событий, моя программа должна закрыть:Завершить работу потока, когда пользователь нажимает кнопку «X» в форме

(1). Пользователь нажимает кнопку «X» в mainFrm (так что mainFrm.FormClosing поднимается)

(2). события (назовем его «connectionClosed») поднимается в Objecta

Таким образом, независимо от того, что, если (1) или (2) срабатывает, эта цепь событий всегда должен включать:

  1. прекращение любого объекта, лежащего в основе потока. (Я уже знаю, как изящно завершает их с кодом, расположенным внутри Objecta)
  2. Конечно, MainFrm ценой закрытия

Какой самый лучший способ решить эту проблему?

ЭТО КАК РЕШИТЬ IT

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

В классе формы:

... 
this.FormClosed += objectA.kill; 
objectA.connectionClosed +=closeForm; 
... 

private void closeForm(object sender, FormClosedEventArgs e) 
{ 
    try 
    { 
     this.Invoke(new MethodInvoker(delegate { this.Close(); })); 
    } 
    catch { } 
} 

В классе Objecta в:

... 
//connectionClosed is raised in different parts of objectA's threads code 
connectionClosed += killClient; 
... 

public void killClient(object sender, FormClosedEventArgs e) 
{ 
    //event should go past this point just once 
    if (!_connectionClosed) 
    { 
     _connectionClosed = true; 

     try 
     { 
      ... //close connection killing all threads 
     } 
     catch { } 
    } 
} 

Согласно (1) и (2) usecases заявил в начале, это то, что должно произойти (если я не ошибаюсь)

(1). Пользователь нажимает кнопку «X» -> закрывается форма -> formclosed поднят -> выполняется делегат метода objectA.kill -> внутренние потоки поднимают некоторые события закрытого соединения, которые будут запускать больше выполнения objectA.kill, но это не принесет никакого ущерба to volatile bool _connectionClosed (конечно, поскольку есть попытка try/catch, которая будет работать в любом случае, но мне просто не нужно больше выполнять такой код) -> потоки завершаются единственным полным выполнением объектаA.kill -> SUCCESS

(2). Сервер закрывает соединение или сетевую ошибку. - Внутренние потоки objectA обнаруживают ошибку соединения и увеличивают несколько соединений. Закрытые события -> разные потоки будут пытаться выполнить делегат метода objectA.kill. -> в то же время в основной форме выполняется closeForm, закрывая форму -> это также запускает другое выполнение objectA.kill (спасибо this.FormClosed + = _client.killClient;) -> снова, это не наносит вреда, поскольку _connectionClosed volatile bool позволит только одному потоку фактически выполнить код (первый, который поднял событие) -> потоки изящно прекращаются единственным полным выполнением объекта A.убить -> УСПЕХ

Следующий шаг должен найти более удобный способ, так что connectionClosed может быть вырос только один раз, я Google за это прямо сейчас =)

+0

Совет: В 'connectionClosed', вызовите' mainFrm.Close() '. Тогда это упрощает первую ситуацию. –

ответ

1

Что проблема с вызовом Close() на событие? Если вы переключаете логическое значение на FormClose, значит, следующие события можно игнорировать. Используйте только FormClosing, если вы хотите прервать действие пользователя перед закрытием формы. С помощью FormClosing вы можете предотвратить закрытие форума. Это чаще всего используется, если некоторые данные не сохраняются и вы спрашиваете пользователя, хочет ли он закрыть форму без сохранения, иначе вы отмените закрытие.

Так что не используйте FormClosing, используйте FormClosed для экземпляров очистки.

+0

Это помогло разобраться в решении, спасибо. –

1

Я не уверен, что это может помочь вам устранить вашу проблему, но вы должны проконсультироваться с приведенным ниже кодом. Это пример, который изменяет цвет формы с помощью объекта потока. Автомат Form1 закрыт, когда событие _objectA.DoSomething поднято 20 раз (if(count > 20): вы можете рассмотреть это действие, как ваше событие connectionClosed поднял); или вы можете закрыть Form1 в любое время, пока поток работает, нажав кнопку «X» Form1.

public delegate void ObjectADoSomethingEventHandler(object sender, ObjectADoSomethingEventArgs e); 
public class ObjectADoSomethingEventArgs : EventArgs 
{ 
    public int Value { get; private set; } 
    public ObjectADoSomethingEventArgs(int value) 
    { 
     Value = value; 
    } 
} 


public class ObjectA 
{ 
    public event ObjectADoSomethingEventHandler DoSomething; 
    protected void OnDoSomething(int value) 
    { 
     if (DoSomething != null) 
      DoSomething(this, new ObjectADoSomethingEventArgs(value)); 
    } 

    public event EventHandler Closed; 
    protected void OnClosed() 
    { 
     if (Closed != null) 
      Closed(this, new EventArgs()); 
    } 


    private BackgroundWorker _worker; 
    public ObjectA() 
    { 
     _worker = new BackgroundWorker(); 
     _worker.DoWork += new DoWorkEventHandler(_objectA_DoWork); 
     _worker.ProgressChanged += new ProgressChangedEventHandler(_objectA_ProgressChanged); 
     _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_objectA_RunWorkerCompleted); 
     _worker.WorkerReportsProgress = true; 
     _worker.WorkerSupportsCancellation = true; 
    } 

    public void Start() 
    { 
     _worker.RunWorkerAsync(); 
    } 

    public void Kill() 
    { 
     if (_worker != null && _worker.IsBusy) 
     { 
      _worker.CancelAsync(); 
     } 
    } 

    private void _objectA_DoWork(object sender, DoWorkEventArgs e) 
    { 
     int count = 0; 
     while (true) 
     { 
      _worker.ReportProgress(count); 
      count++; 
      if (count > 20) 
      { 
       return; // exit thread. 
      } 

      if (_worker.CancellationPending) 
      { 
       e.Cancel = true; 
       return; // Thread cancelled. 
      } 
      Thread.Sleep(500); 
     } 
    } 

    private void _objectA_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     OnDoSomething(e.ProgressPercentage); 
    } 

    private void _objectA_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     OnClosed(); 
    } 
} 


public partial class Form1 : Form 
{ 
    private ObjectA _objectA; 

    public Form1() 
    { 
     InitializeComponent(); 
     _objectA = new ObjectA(); 
     _objectA.DoSomething += _objectA_DoSomething; 
     _objectA.Closed += _objectA_Closed; 
     _objectA.Start(); 
    } 

    private void Form1_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     _objectA.Kill(); 
    } 

    private int _red = 128; 
    private int _green = 128; 
    private int _blue = 128; 
    void _objectA_DoSomething(object sender, ObjectADoSomethingEventArgs e) 
    { 
     _red += 15; 
     if (_red > 255) _red = 128; 
     _green -= 15; 
     if (_green < 0) _green = 128; 
     _blue += 15; 
     if (_blue > 255) _blue = 128; 
     this.BackColor = Color.FromArgb(_red, _green, _blue); 
     this.Text = string.Format("Count = {0}", e.Value); 
    } 

    void _objectA_Closed(object sender, EventArgs e) 
    { 
     Close(); 
    } 
} 
+0

спасибо за ваше время, но на данный момент я не использую BackgroundWorker, а напрямую Threads. В любом случае, это интересно, я внимательно прочитал его, возможно, я перейду к BackgroundWorker, если возникнут другие проблемы при прямом управлении потоками. –

+0

BackgroundWorker также является нитью. Поэтому вы можете легко заменить BackgroundWorker на прямой Thread. Я использую BackgroundWorker в качестве примера для более сжатого кода, чем непосредственно для создания и управления объектом Thread. Кроме того, при использовании BackgroundWorker вы можете напрямую вызывать методы и свойства объектов, связанных с формой (не через метод Invoker). –

 Смежные вопросы

  • Нет связанных вопросов^_^