Модераторы: Partizan, gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Асинхронные вызовы, почему не убиваются потоки? 
V
    Опции темы
Experimenter
Дата 24.7.2007, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



Доброго здоровья, уважаемые!
Такой теоретический вопрос. Но сначала код:

Код

using System;
using System.Data;
using System.Threading;

    public delegate void Async(int number);

class AsyncCallbackConsole
{
    static void Main()
    {
        Console.WriteLine("***AsyncCallback Console***\r\n");
 
        Async a = new Async(AsyncExecuter);
        IAsyncResult iar;
        for (int i = 0; i < 50; i++)
            iar = a.BeginInvoke(i + 1, new AsyncCallback(Completion), a);

        Console.ReadLine();
    }

    static void AsyncExecuter(int x)
    {
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + ":--> Работа потока {0} начата", x);

        Random rand = new Random();
        Thread.Sleep(rand.Next(1, 30) * 1000);
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss") + ":--> Поток {0} завершил работу", x);
    }

    static void Completion(IAsyncResult iar)
    {
        Async a = (Async)iar.AsyncState;
        a.EndInvoke(iar);
        a = null;
    }


Запускаем и видим, что потоки поочередно поднимаются и завершаются (по крайней мере, так консоль сообщает). Но по завершении всех потоков в Таск Менеджере напротив приложения вижу число потоков 29. Минуты через три убиваются, видимо сборщиком мусора.
Вопрос такой: что не так и как этого избежать? Избежать именно в этом контексте (BeginInvoke и EndInvoke), System.Threading я уже освоил.


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
mr.DUDA
Дата 24.7.2007, 12:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 110
Всего: 232



Дело в том, что BeginInvoke работает черед ThreadPool, который выделяет потоки по необходимости. Если выделено одновременно 29 потоков, которые ждут N секунд и потом завершаются, ThreadPool справедливо предполагает что среднее кол-во потоков ещё некоторое время может оставаться на прежнем уровне, и держит их живыми. Потом убивает через какое-то время. Проверить эту гипотезу можно, вызвав в самом начале ThreadPool.SetMaxThreads(число, число).


--------------------
user posted image
PM MAIL WWW   Вверх
Experimenter
Дата 24.7.2007, 13:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



mr.DUDA, да, кстати, это я уже проверял, так и работает, но я полагал, что потоки должны убиваться, когда они выполняют отведенный им код. Почему, когда я то же самое выполняю через System.Threading, никакие хвосты не остаются, если метод, выполняемый потоком, завершен? Дело в том, что я пишу приложения, которые работают круглосуточно, не хотелось бы допускать подобных нагрузок на систему.


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
mr.DUDA
Дата 24.7.2007, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 110
Всего: 232



Цитата(Experimenter @  24.7.2007,  13:05 Найти цитируемый пост)
 потоки должны убиваться, когда они выполняют отведенный им код

Создание потока - действие, занимающее относительно большое время, поэтому в многопоточных приложениях в случае частого создания потоков они (потоки) используются повторно. На практике это означает, что функция потока запускается не пользователем а классом, назовём его пулом потоков, который ожидает вызова метода а-ля StartThread или QueueUserWorkItem, принимающего делегат (в С++ - указатель на функцию) на метод, выполняющий реальную задачу. Пул потоков проверяет, есть ли свободные потоки ("спящие" в данный момент) и если нет - создаёт новый поток. Если есть свободные - с помощью объектов синхронизации "пробуждает" поток (например, тот может ждать в WaitForSingleObject) и через общую переменную передаёт потоку указатель на функцию, которую необходимо выполнить. На этом роль пула потоков заканчивается, далее выбранный поток вызывает функцию и по выходу из неё "засыпает" до следующего раза.

В таких случаях как веб-приложения ASP.NET, создание кучи потоков уступает использованию пула, т.к. реально потоков может потребоваться много и часто. Кроме того, пул потоков позволяет ставить запросы на создание потока в очередь в случае, если достигнут лимит одновременно выполняемых потоков. Ведь на самом деле, все потоки выполняются на одном, в лучшем случае - двух-четырёх процессорах, и одновременно выполняемые 50..100 потоков ощутимо нагрузят систему.


--------------------
user posted image
PM MAIL WWW   Вверх
0000
Дата 27.7.2007, 13:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 208
Регистрация: 11.7.2006
Где: Нижний Новгород

Репутация: 2
Всего: 5



а почему при создании потока таким образом может вылазить NotSupportedException

Код

public void Send()
        {
            State stateObject = new State();
            SendDelegate sd = new SendDelegate(this.doSend);
            IAsyncResult ar;
            stateObject.ActiveDelegate = sd;
            ar = sd.BeginInvoke(new AsyncCallback(this.SendCallBack), stateObject);
        }
        private void SendCallBack(IAsyncResult ar)
        {
            State sd = (State)ar.AsyncState;
            OrderEventArgs e = new OrderEventArgs();
            sd.ActiveDelegate.EndInvoke(ar);
            if (sd.IsSucces)
            { e.Message = "Отправка завершена успешно"; }
            else { e.Message = "Ошибка при отправке"; }
            if (OnSended != null)
                OnSended(sd, e);
        }
        private void doSend()
        {
            if (this.fname != null)
            {
                FtpClient ftp = new FtpClient();
                bool temp = ftp.Upload(this.fname + ".xml");
            }
        }


и со в другом объекте вызываю этот Send...
вылетает на нем эксепшн
PM MAIL ICQ   Вверх
Experimenter
Дата 27.7.2007, 14:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



0000, МСДН смотрел?
Цитата

The exception that is thrown when an invoked method is not supported, or when there is an attempt to read, seek, or write to a stream that does not support the invoked functionality. 




--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
0000
Дата 27.7.2007, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 208
Регистрация: 11.7.2006
Где: Нижний Новгород

Репутация: 2
Всего: 5



смотрел конечно...
тока не понимаю как к моему это привязано..
тут отсылаю файл, который в этот момент никак не читается, никак не обрабатывается

я даже просто отдельно если создам объект A a = new A();
и вызываю a.Send()
все равно выскакивает....а в новом объекте таким образом привязки к файлу вообще нет и в помине

Это сообщение отредактировал(а) 0000 - 27.7.2007, 14:15
PM MAIL ICQ   Вверх
Experimenter
Дата 27.7.2007, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



mr.DUDA, неубедительно. Вижу прямую противоположность в Таск Менеджере. При использовании System.Threading число потоков сначала возрастает по мере их загрузки, потом уменьшается по мере выполнения ими своих задач. Причем НЕМЕДЛЕННО! При асинхронных вызовах потоки висят в ТМ минуты три, не меньше. Если и там и тут используется ThreadPool, то почему он ведет себя по-разному? Значит еще какой-то фактор влияет!

Проверяется легко. Вместо кода:
Код

static void Main()
    {
        Console.WriteLine("***AsyncCallback Console***\r\n");
 
        Async a = new Async(AsyncExecuter);
        IAsyncResult iar;
        for (int i = 0; i < 50; i++)
            iar = a.BeginInvoke(i + 1, new AsyncCallback(Completion), a);
        Console.ReadLine();
    }


ставим:
Код

static void Main()
    {
        //Console.WriteLine("***AsyncCallback Console***\r\n");
 
        //Async a = new Async(AsyncExecuter);
        //IAsyncResult iar;
        for (int i = 0; i < 50; i++)
        {
            //iar = a.BeginInvoke(i + 1, new AsyncCallback(Completion), a);
            Thread thr = new Thread(new ParametrizedThreadStart(AsyncExecuter));
            thr.Start(i)
        }
        Console.ReadLine();
    }


вместо:
Код

static void AsyncExecuter(int x)

ставим:
Код

static void AsyncExecuter(object x)


Запускаем консоли с разным кодом, запускаем Диспетчер задач и чувствуем разницу...

P.S.: может в случае с асинхронными вызовами финализатор принудительно нужно вызывать каким-то образом? Надо будет попробовать...

Добавлено через 7 минут и 35 секунд
0000, трудно вот так сказать сразу, я в МСДН в описании классов встречал такие приколы: мол, эта фича описана, но не поддерживается в текущей версии Фреймворка. Видимо на будущее заготовили...

Это сообщение отредактировал(а) Experimenter - 27.7.2007, 17:54


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
stab
Дата 28.7.2007, 06:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



Цитата(Experimenter @  27.7.2007,  21:50 Найти цитируемый пост)
Если и там и тут используется ThreadPool

дуда как раз обратное говорит, что для асинхронных делегатов используется пул, а для прямой работы с Thread никаких пулов нет.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Experimenter
Дата 28.7.2007, 06:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



mr.DUDA, stab, то есть вы хотите сказать, что это нормальная ситуация, и нечего тут париться, да?


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
stab
Дата 28.7.2007, 06:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



да, для асинхронных делегатов это абсолютно нормальная-рабочая ситуация.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
0000
Дата 30.7.2007, 10:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 208
Регистрация: 11.7.2006
Где: Нижний Новгород

Репутация: 2
Всего: 5



то есть как я понимаю использование асинхронных вызовов от простого Thread.Start() отличается тем, что первые используют пул потоков???
а аналогично ли действует ThreadPool.QueueUserWorkItem(new WaitCallback(method), state);???
и преимущества его в чем?? в том, что ему не всегда необходимо создавать новый поток, а можно пробудить спящий???
PM MAIL ICQ   Вверх
stab
Дата 31.7.2007, 08:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: 22
Всего: 48



0000, на все вопросы ответ да.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
0000
Дата 31.7.2007, 08:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 208
Регистрация: 11.7.2006
Где: Нижний Новгород

Репутация: 2
Всего: 5



stab, коротко и ясно!! Спасибо =)
PM MAIL ICQ   Вверх
Experimenter
Дата 31.7.2007, 09:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 430
Регистрация: 8.5.2007
Где: Уфа

Репутация: 8
Всего: 17



Закрываем тему


--------------------
public Zlo FromTwoEvilsChooseSmaller(Zlo zlo1, Zlo zlo2){
    if(zlo1 < zlo2) return zlo1;
    else if(zlo1 > zlo2) return zlo2;
    else throw new Exception("Kill yourself by the wall"); }
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема »


 




[ Время генерации скрипта: 0.0594 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.