запуск по таймеру
От: Аноним  
Дата: 25.01.11 22:43
Оценка:
на форме есть таймер (System.Windows.Forms.Timer), он вызывается каждые X секунд и дёргает асинхронную функцию:


        private void timerFindDevice_Tick(object sender, EventArgs e)
        {
            DiscoveryMyDeviceDelegate discoveryDelegate = new DiscoveryMyDeviceDelegate(DiscoverDevice);
            discoveryDelegate.BeginInvoke(null, null);
        }


которая неизвестно сколько будет выполняться. Мне нужно чтобы при следующем тике таймера проверялось, закончился ли асинхронный запрос и если нет, то не вызывать эту функцию (DiscoverDevice). Можно конечно сделать глобальную переменную и проверять, но как сделать правильно?
Re: запуск по таймеру
От: Lloyd Россия  
Дата: 25.01.11 22:50
Оценка: :)
Здравствуйте, Аноним, Вы писали:

А>которая неизвестно сколько будет выполняться. Мне нужно чтобы при следующем тике таймера проверялось, закончился ли асинхронный запрос и если нет, то не вызывать эту функцию (DiscoverDevice). Можно конечно сделать глобальную переменную и проверять, но как сделать правильно?


Если красиво — то написать функцию, которая принимает на вход функцию и возвращает функцию, которая будучи вызвана, вызывает переданную функцию только в том случае, если функция уже закончила работу. И в обработчике таймера дергать это функцию.

Надеюсь, понятно объяснил?
Re[2]: запуск по таймеру
От: Lloyd Россия  
Дата: 25.01.11 23:14
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Если красиво — то написать функцию, которая принимает на вход функцию и возвращает функцию, которая будучи вызвана, вызывает переданную функцию только в том случае, если функция уже закончила работу. И в обработчике таймера дергать это функцию.


L>Надеюсь, понятно объяснил?


Как-то так:
using System;
using System.Threading;

class Program
{
    private static readonly Action<int> PrintOnceInTime = FunctionalHelpers.OnceInTime<int>(Print);

    private static void Print(int i)
    {
        Console.WriteLine(i);
        Thread.Sleep(1000);
    }

    static void Main()
    {
        for (int i = 0; i < 10000; i++)
        {
            PrintOnceInTime.BeginInvoke(i, null, null);
            Thread.Sleep(250);
        }
    }
}

public static class FunctionalHelpers
{
    public static Action<T> OnceInTime<T>(Action<T> action)
    {
        bool isRuning = false;
        return t =>
        {
            if (isRuning)
                return;

            try
            {
                isRuning = true;
                action(t);
            }
            finally
            {
                isRuning = false;
            }

        };
    }
}
Re: запуск по таймеру
От: Аноним  
Дата: 26.01.11 06:47
Оценка:
Здравствуйте, Аноним, Вы писали:
А>
А>        private void timerFindDevice_Tick(object sender, EventArgs e)
А>        {
А>            DiscoveryMyDeviceDelegate discoveryDelegate = new DiscoveryMyDeviceDelegate(DiscoverDevice);
А>            discoveryDelegate.BeginInvoke(null, null);
А>        }
А>


Вообще говоря вызов BeginInvoke без соответствующего EndInvoke вызывает утечку ресурсов, так что по аккуратнее с этим.
Re[3]: запуск по таймеру
От: eos  
Дата: 26.01.11 15:07
Оценка: +1
Здравствуйте, Lloyd, Вы писали:

L>
L>public static class FunctionalHelpers
L>{
L>    public static Action<T> OnceInTime<T>(Action<T> action)
L>    {
L>        bool isRuning = false;
L>        return t =>
L>        {
L>            if (isRuning)
L>                return;

L>            try
L>            {
L>                isRuning = true;
L>                action(t);
L>            }
L>            finally
L>            {
L>                isRuning = false;
L>            }

L>        };
L>    }
L>}

L>


Что-то мне подсказывает, что Ваш метод не потокобезопасен. Я бы предложил немного другую реализацию:

int isRuning = 0;
return t =>
{
    if (Interlocked.CompareExchange(isRuning, 1, 0) == 1)
        return;

    try
    {
        action(t);
    }
    finally
    {
        Interlocked.Exchange(1, 0);
    }
};


Поправьте если не прав.

P.S. писал в браузере, так что могут быть ошибки в коде, но смысл постарался сохранить
Re: запуск по таймеру
От: Spiceman  
Дата: 26.01.11 20:10
Оценка:
Здравствуйте, Аноним, Вы писали:

А>на форме есть таймер (System.Windows.Forms.Timer), он вызывается каждые X секунд и дёргает асинхронную функцию:


А зачем используете именно этот таймер? Его особенность в том, что он функцию вызывает в потоке формы и из нее удобно обращаться к элементам формы.

А>Мне нужно чтобы при следующем тике таймера проверялось, закончился ли асинхронный запрос и если нет, то не вызывать эту функцию (DiscoverDevice). Можно конечно сделать глобальную переменную и проверять, но как сделать правильно?


Используйте System.Threading.Timer:
System.Threading.Timer timer1 = new System.Threading.Timer(_ => DiscoverDevice(), null, 0, period);
Re[2]: запуск по таймеру
От: Lloyd Россия  
Дата: 26.01.11 20:30
Оценка:
Здравствуйте, Spiceman, Вы писали:

А>>Мне нужно чтобы при следующем тике таймера проверялось, закончился ли асинхронный запрос и если нет, то не вызывать эту функцию (DiscoverDevice). Можно конечно сделать глобальную переменную и проверять, но как сделать правильно?


S>Используйте System.Threading.Timer:

S>
S>System.Threading.Timer timer1 = new System.Threading.Timer(_ => DiscoverDevice(), null, 0, period);
S>


А это точно поможет?
Re[3]: запуск по таймеру
От: Spiceman  
Дата: 26.01.11 21:39
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>А это точно поможет?


Мда, что-то я ступил, таймер же также срабатывает — каждый раз просто создает новый поток из пула потоков. Тогда только с помощью флага разруливать.

А вообще мы еще не знаем какое поведение требуется ТС. Если таймер выполняется каждые 2 секунды, а метод выполнялся 3 секунды, то следующий вызов должен быть сразу после выполнения метода или через 4 секунды от начала работы таймера?
Re[4]: запуск по таймеру
От: Lloyd Россия  
Дата: 26.01.11 21:48
Оценка:
Здравствуйте, Spiceman, Вы писали:

S>Мда, что-то я ступил, таймер же также срабатывает — каждый раз просто создает новый поток из пула потоков. Тогда только с помощью флага разруливать.


S>А вообще мы еще не знаем какое поведение требуется ТС. Если таймер выполняется каждые 2 секунды, а метод выполнялся 3 секунды, то следующий вызов должен быть сразу после выполнения метода или через 4 секунды от начала работы таймера?


Через 4.
Re[2]: запуск по таймеру
От: Аноним  
Дата: 27.01.11 00:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Вообще говоря вызов BeginInvoke без соответствующего EndInvoke вызывает утечку ресурсов, так что по аккуратнее с этим.


а можно пример как правильно?
Re: запуск по таймеру
От: x64 Россия http://x64blog.name
Дата: 27.01.11 04:01
Оценка:
А>Можно конечно сделать глобальную переменную и проверять, но как сделать правильно?

Правильно это когда используются механизмы синхронизации, очевидно же. Самый простой способ через интерлокеры (см. метод CompareExchange). Ещё можно использовать мониторы (см. метод TryEnter).
JID: x64j@jabber.ru
Re[3]: запуск по таймеру
От: Аноним  
Дата: 27.01.11 08:26
Оценка:
Здравствуйте, Аноним, Вы писали:

А>а можно пример как правильно?


На делегатах как-то так наверное (скорее всего можно и покороче оформить):
        private DiscoveryMyDeviceDelegate DiscoveryDelegate
        {
            get 
            {
                if (discoveryDelegate == null)
                { 
                    discoveryDelegate = new DiscoveryMyDeviceDelegate(DiscoverDevice);
                }

                return discoveryDelegate;
            }
        }

        private DiscoveryMyDeviceDelegate discoveryDelegate;
        private IAsyncResult discoveryAsyncResult;

        private void timerFindDevice_Tick(object sender, EventArgs e)
        {
            if (discoveryAsyncResult == null)
            {
                 discoveryAsyncResult = DiscoveryDelegate.BeginInvoke(null, null);
            }
            else if (discoveryAsyncResult.IsCompleted)
            {
                 // try
                 DiscoveryDelegate.EndInvoke(discoveryAsyncResult);  // Если в процессе работы были исключения, будут переброшены здесь
                 // catch (Exception ex) { ... }

                 discoveryAsyncResult = DiscoveryDelegate.BeginInvoke(null, null);
            }
        }


Но раз уж у вас WinForms, скорее всего проще будет воспользоваться компонентом BackgroundWorker — у него есть свойство IsBusy и событие Completed, которое генерируется в контексте GUI потока, что может быть весьма полезным.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.