Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Не убивается процесс


Автор: sindi8 1.8.2005, 16:05
Всем привет.
Никак не могу одолеть грабли - создаю я ассинхронный процесс, процесс запускается и отрабатывает что от него требуется. Но при закрытии формы приложение остается висеть в списке запущенных процессов в task manager-e. Не подскажите как с этим бороться? Делаю следующим образом:

Код

delegate void ScanDriveDelegate(string dir, int i);
...
private void Init()
{
ScanDriveDelegate scanDrive = new ScanDriveDelegate(ScanDrive);
scanDrive.BeginInvoke(comboBox1.Items[comboBox1.SelectedIndex].ToString(), 0, null, null);
}

private void ScanDrive(string dir, int i)
{
...
//Рекурсия
...
}

Автор: arilou 1.8.2005, 16:16
Ты не мог бы привести полный текст программы?

Автор: sindi8 1.8.2005, 16:39
Выглядит все примерно так (выбросил блок сгенеренный студией и Dispose):
Код

public class Form1 : System.Windows.Forms.Form
{
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.ListBox listBox1;
    private System.Windows.Forms.Label label2;
    private System.Windows.Forms.ComboBox comboBox1;
    private System.Windows.Forms.ProgressBar progressBar1;
    delegate void ScanDriveDelegate(string dir, int i);
    private System.ComponentModel.Container components = null;

    public Form1()
    {
        InitializeComponent();
        //Список доступных дисков
        string[] drives = Directory.GetLogicalDrives();
        foreach (string s in drives)
            comboBox1.Items.Add(s);
    }

    [STAThread]
    static void Main() 
    {
        Application.Run(new Form1());
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        progressBar1.Visible = false;
        button1.Enabled = false;
        comboBox1.Enabled = false;
        //Запускаю процесс
        ScanDriveDelegate scanDrive = new ScanDriveDelegate(ScanDrive);
        scanDrive.BeginInvoke(comboBox1.Items[comboBox1.SelectedIndex].ToString(), 0, null, null);
    }

    private void ScanDrive(string dir, int i)
    {
        try
        {
            string[] dirs;
            if (i==0)
            {
                dirs = new string[] {dir};
                Directory.GetDirectories(dir);
            }
            else
                dirs = Directory.GetDirectories(dir);
            foreach (string s in dirs)
            {
                try
                {
                    //Ищу файлы ворда
                    string[] files = Directory.GetFiles(s,"*.doc");
                    foreach (string filepath in files)
                        listBox1.Items.Add(filepath);
                    label1.Text = s;
                    DirectoryInfo di = new DirectoryInfo(s);
                    if ((di.Attributes & FileAttributes.Directory) != 0)
                    {
                        ScanDrive(s, i+1);
                    }
                }
                catch {}
            }
            if (i==0)
            {
                label1.Text = "Поиск файлов завершен. Идет обработка...";
                    progressBar1.Visible = true;
                    progressBar1.Maximum = listBox1.Items.Count;
                    int counter=0; //Указатель текущей позиции прогресбара
                    StreamWriter sw = File.CreateText("tmp.log");
                    foreach (string filepath in listBox1.Items)
                    {
                        counter++;
                        progressBar1.Value = counter;
                        try
                        {
                        //Курочу найденные файлы
                        }
                        catch
                        {
                            //Не удается изменить параметры файла. Записываем в лог-файл
                            sw.WriteLine(filepath);
                        }
                    }
                    sw.Close();
                    label1.Text = "Операция завершена.";
                    progressBar1.Visible = false;
                    button1.Enabled = true;
                    comboBox1.Enabled = true;
                }
            }
            catch (Exception ex)
            {
                //Должно выскакивать когда произошло обращение к несуществующей области
                MessageBox.Show(ex.Message,"Ошибка");
                button1.Enabled = true;
                comboBox1.Enabled = true;
            }
        }
    }

Если нужен полностью рабочий пример - могу скинуть в мыло.

Автор: mr.DUDA 1.8.2005, 17:58
Скорее всего, вызываемый асинхронно поток по какой-то причине не завершает работу, а остаётся висеть.
Добавлено @ 18:00
З.Ы, только что увидел:
Цитата
label1.Text = "Поиск файлов завершен. Идет обработка...";
progressBar1.Visible = true;
progressBar1.Maximum = listBox1.Items.Count;

Так делать нельзя. С формой и контролами можно работать только в их "родных" потоках, а из других потоков - только с использованием метода Control.Invoke. Возможно, именно по этой причине глюки.

Автор: sindi8 1.8.2005, 18:11
Спасибо за наводку. Не знал про это. Попробую, может действительно из-за этого.
З.Ы. правда думал что сборщик мусора ВСЕ прибивает при завершении приложения. smile

Автор: arilou 1.8.2005, 18:28
Цитата(sindi8 @ 1.8.2005, 18:11)
правда думал что сборщик мусора ВСЕ прибивает при завершении приложения

GC тут ни при чем. Может не работать из-за того, что в потоке Exception происходит. Он подвисает, и из-за этого приложение не завершает работу.

Автор: mr.DUDA 1.8.2005, 23:01
arilou, вполне может висеть потому, что форма вроде как бы уже закрывается (и может даже закрылась), а в это время к ней обращается др. поток с попыткой подвинуть прогрессбар... Или любой другой потенциальный дедлок. Кстати, в .NET 2.0 такие "наезды" на контрол (им. ввиду код, взаимодействующий с контролом или формой из др. потока) приводят к exception -- имхо, здравая идея. Как известно, в .NET все контролы и формы реализованы на "старом добром" WinAPI, а он шуток не любит...

Автор: -ser- 2.8.2005, 07:06
Цитата
при закрытии формы приложение остается висеть в списке запущенных процессов в task manager-e

может быть вас смущает в task manager-e в списке запущенных процессов ваш <project_name>.vshost.exe?
так это от самой студии (если вы используете 2005), он всегда там есть, пока проект открыт. я попробовал ваш код у себя, все корректно отрабатывает (разумеется с вызовом Invoke на контролах, о чем упомянул mr.DUDA. ошибка сия непозволительная в .NET 2.0, но как я полагаю, дело не в этом, поскольку форму вы закрыть все же смогли).

и попробовал ваш код как есть без каких-либо изменений вообще в студии 2003. в task manager-e в списке запущенных процессов появляется <project_name>.exe. при закрытии формы он благополучно исчезает.

Автор: sindi8 2.8.2005, 12:18
На счет понимания task manager-а проблем не испытываю.
А вот на счет использования Control.Invoke есть вопрос - правильно ли я понял, что мне нужно написать делегат, в котором бы как раз происходило изменение этих самый свойств контролов? и каким образом этот делегат правильно вызывать: грубо говоря так
Код

label1.Invoke(tmpDelegate);

а в tmpDelegate прописать все нужные изменения контролов основного потока?

Цитата
Может не работать из-за того, что в потоке Exception происходит.

Так в том-то и дело, что все отрабатывается, прогрессбар бегает, значения label1 изменяются ну и т.д. Т.е. внешне проложение работает вполне корректно. Дело по всей вероятности видимо именно в этих Invoke, о которых говорил mr.DUDA и другие.

Цитата
и попробовал ваш код как есть без каких-либо изменений вообще в студии 2003. в task manager-e в списке запущенных процессов появляется <project_name>.exe. при закрытии формы он благополучно исчезает.

Ну если бы все было так же гладко - я думаю данная тема бы не появилась.

Автор: arilou 2.8.2005, 12:23
Да, именно так. Чтобы не создавать лишних делегатов, можно использовать, например, делегат MethodInvoker

Код

MethodInvoker mi = new MethodInvoker(controlFunction);
label1.Invoke(mi);


где controlFunction - та ф-ция, в которой находится код, изменяющий свойства контрола. Кстати, можно не заморачиваться, и вызызывать Form.Invoke, т.к. на форме не может быть контролов, созданных из других потоков. кроме как создавшего форму.
Добавлено @ 12:29
Цитата(sindi8 @ 2.8.2005, 12:18)
Так в том-то и дело, что все отрабатывается

Тут дело хитрое - я заметил, что если ты вызываешь метод асинхронно, и если в нем исключение происходит, то тогда никто тебе о нем не сообщает, по крайней мере, так было в моем сценарии. Потко просто тихо умирал, и программа не стопалась. Наверное, именно по этой причине .NET 2.0 тебе не сообщил, что у тебя InvalidOperationException, когда ты контролы модифицировал из другого потока.

Автор: sindi8 2.8.2005, 12:31
Спасибо всем. Сейчас буду тестировать.

Да, и у меня кстати .NET 1.1. По непонятным для меня причинам мне на ящик не пришло подтверждение заявки на 2005-ю студию и соответственно студия не пришла.

Автор: arilou 2.8.2005, 12:33
Да, именно так smile Сча подредактирую
Цитата(sindi8 @ 2.8.2005, 12:31)
но вы случаем не опечатались


Добавлено @ 12:41
Цитата(sindi8 @ 2.8.2005, 12:31)
Да, и у меня кстати .NET 1.1

Тогда все понятно, в 1.1. такой фичи не было (насчет перехвата таких ситуаций).

Автор: sindi8 2.8.2005, 12:58
На счет MethodInvoker - его видимо можно использовать только в том случае, если функция, выполняющая нужные дествия с контролами, без параметров. Если есть параметры - нужно все таки делегаты самому создавать. Или я заблуждаюсь?

Автор: arilou 2.8.2005, 12:59
Так и есть.

Автор: sindi8 2.8.2005, 13:36
Проблема решена. Всем большое спасибо. Все решилось путем использования Control.Invoke и описанием делегатов, ссылающихся на функции обработки необходимый действий над контролами.

Вопрос на засыпку smile Как отловить завершение ассинхронного потока? Я отлавливал внутри самой функции. Извне как-то можно? EndInvoke имеет к этому отношение?

Автор: arilou 2.8.2005, 13:47
Вот нарыл в MSDN:

Код

        IAsyncResult aResult = sampleDelegate.BeginInvoke(callParameter, null, null);

        //Wait for the call to complete
        aResult.AsyncWaitHandle.WaitOne();

        callResult = sampleDelegate.EndInvoke(aResult);


Цитата(sindi8 @ 2.8.2005, 13:36)
ассинхронного процесса

Лучше говорить "асинхронного вызова", а то я, например, сразу не понял, о чем речь идет в самом первом посте smile

Автор: sindi8 2.8.2005, 14:12
MSDN читал. Пробовал воспроизвести нечто подобное, но приложение залипало на
Код

aResult.AsyncWaitHandle.WaitOne();

и как-то это сделать нормально чтоб работало у меня не получилось. В общем - если кто поделится парой строк - буду признателен. Хотелось бы узнать, как выполнить какую-то функцию по завершении работы потока.
З.Ы. слово "поток" надеюсь более корректно smile

Автор: arilou 2.8.2005, 14:25
Вот. Тут код на VB.NET, но я думаю, ты без проблем его заюзаешь. Он реализует то, что тебе надо - fire and foget - вызвал и забыл smile

Код

Imports System.Threading

Public Class AsyncHelper
  Class TargetInfo
    Friend Sub New(ByVal d As [Delegate], ByVal arguments As Object(), Optional ByVal cbk As AsyncCallback = Nothing)
      Target = d
      Args = arguments
      CallBack = cbk
    End Sub

    Friend Target As [Delegate]
    Friend Args As Object()
    Public CallBack As AsyncCallback
  End Class

  Private Shared _dynamicInvokeShim As New WaitCallback(AddressOf DynamicInvokeShim)

  Public Shared Sub FireAndForget(ByVal d As [Delegate], ByVal cbk As AsyncCallback, ByVal ParamArray args As Object())
    ThreadPool.QueueUserWorkItem(_dynamicInvokeShim, New TargetInfo(d, args, cbk))
  End Sub

  Shared Sub DynamicInvokeShim(ByVal o As Object)
    Dim ti As TargetInfo = o
    Try
      ti.Target.DynamicInvoke(IIf(ti.Args Is Nothing, New Object() {}, ti.Args))
    Catch ex As Exception
      MsgBox(ex.ToString, , "AsyncHelper.Delegate:")
    End Try

    If Not ti.CallBack Is Nothing Then
      Try
        ti.CallBack.DynamicInvoke(New Object() {Nothing})
      Catch ex As Exception
        MsgBox(ex.ToString, , "AsyncHelper.Callback:")
      End Try
    End If
  End Sub
End Class

Автор: sindi8 2.8.2005, 17:24
arilou, спасибо. Думаю разберусь.

Автор: mr.DUDA 2.8.2005, 21:57
arilou, Visual Basic.... smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)