[F#] Запуск асинхронных задач с обработкой ошибок
От: cadet354 Россия
Дата: 16.06.10 12:43
Оценка:
Доброго времени,
есть список объектов с координатами для которых надо получить адрес, если его можно определить сервисом (геокодинг).
Список этих объектов довольно большой, хотелось бы обрабатывать асинхронно, с логированием того, что для этого объекта нет адреса (или сервис вернул ошибку).
Сделать асинхронный запрос благодаря многочисленным руководствам, не проблема:
async {
                let request = HttpWebRequest.Create(url)
                let! response = request.AsyncGetResponse()
                use response = response
                let stream = response.GetResponseStream()
                use reader = new StreamReader(stream)
                let! result=  reader.AsyncReadToEnd()
                return (id,result) (* id это id объекта, указывается выше по коду *)
                } 
                |>  Async.Catch
                |>  Async.RunSynchronously
                |>  function
                    | Choice1Of2 (id2,result2)     -> (id2,result2)
                    | Choice2Of2 (ex : exn) -> (id,ex.Message)

проблема в том, что Async.RunSynchronously так же как и Async.StartWithContinuations вызываются немедленно, а мне хотелось бы сформировать лист Async и потом их вызвать c помощью Async.Parallel
Как быть в этой ситуации?
Re: [F#] Запуск асинхронных задач с обработкой ошибок
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 16.06.10 13:01
Оценка:
Здравствуйте, cadet354, Вы писали:

C>проблема в том, что Async.RunSynchronously так же как и Async.StartWithContinuations вызываются немедленно, а мне хотелось бы сформировать лист Async и потом их вызвать c помощью Async.Parallel

C>Как быть в этой ситуации?

1) Почему бы не использовать try внутри async?
2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?
Re[2]: [F#] Запуск асинхронных задач с обработкой ошибок
От: cadet354 Россия
Дата: 16.06.10 13:15
Оценка:
Здравствуйте, achmed, Вы писали:

A>1) Почему бы не использовать try внутри async?

не кошерно
A>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?
а как получить список ошибок?
Re[3]: [F#] Запуск асинхронных задач с обработкой ошибок
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 16.06.10 13:25
Оценка: 4 (1) +1
Здравствуйте, cadet354, Вы писали:

C>Здравствуйте, achmed, Вы писали:


A>>1) Почему бы не использовать try внутри async?

C>не кошерно
даже не знаю что сказать ...

A>>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?

C>а как получить список ошибок?
Parallel вернет список Choice<(string*string),exn>
Re[3]: [F#] Запуск асинхронных задач с обработкой ошибок
От: Jack128  
Дата: 16.06.10 13:30
Оценка: 4 (1)
Здравствуйте, cadet354, Вы писали:

C>Здравствуйте, achmed, Вы писали:


A>>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?

C>а как получить список ошибок?

не понял, в чем проблема то? ПРимерно так (возможны опечатки — не проверял)


let ids = [1;2;3;4]
let results = ids 
|> List.map (fun id -> async {
...
})
|> List.map Async.Catch
|> Async.Parallel 
|> Async.RunSynchronously
|> List.ofArray
 
let errors = results |> List.choose (function
                                     | Choice2Of2 e -> Some e
                                     | _ -> None results)
Re: [F#] Запуск асинхронных задач с обработкой ошибок
От: WolfHound  
Дата: 16.06.10 14:01
Оценка:
Здравствуйте, cadet354, Вы писали:

C>Как быть в этой ситуации?

Использовать правильные языки.
Немерловая версия умеет такое из коробки.
По этим двум примерам думаю все поймешь.
    [TestCaseAttribute(<#
Value(20)
Value(20)
Value(20)
Value(121)
Value(121 20)
Value(20 123)
Value(20 20)
#>)]
    public static Test1(stream : IO.TextWriter) : void
    {
      def fn(n)
      {
        comp async
        {
          if (n < 20)
            returncomp fn(n + 1);
          else
            return n;
        }
      }
      def f(n1, n2)
      {
        comp async
        {
          defcomp n1 = fn(n1);
          defcomp n2 = fn(n2);
          return $"$n1 $n2";
        }
      }
      def li = [fn(1), fn(10), fn(15), fn(121)];
      def ls = [f(121, 1), f(11, 123), f(11, 4)];
      def li = li.Map(_.Start());
      def ls = ls.Map(_.Start());
      li.Map(_.GetResult()).Iter(stream.WriteLine(_));
      ls.Map(_.GetResult()).Iter(stream.WriteLine(_));
    }

http://code.google.com/p/nemerle/source/browse/nemerle/trunk/snippets/ComputationExpressions/Test/AsyncTest.n
GetResult возвращает
http://code.google.com/p/nemerle/source/browse/nemerle/trunk/snippets/ComputationExpressions/ComputationExpressions/Async/AsyncResult.n
    private HttpGet(url : string) : Async[string]
    {
      comp async
      {
        def req = WebRequest.Create(url);
        using (defcomp resp = req.AsyncGetResponse())
        using (stream = resp.GetResponseStream())
        using (reader = StreamReader(stream))
          return reader.ReadToEnd();
      }
    }

http://code.google.com/p/nemerle/source/browse/nemerle/trunk/snippets/ComputationExpressions/AsyncHttp/MainForm.n
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: [F#] Запуск асинхронных задач с обработкой ошибок
От: cadet354 Россия
Дата: 16.06.10 14:18
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, cadet354, Вы писали:


C>>Здравствуйте, achmed, Вы писали:


A>>>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?

C>>а как получить список ошибок?

J>не понял, в чем проблема то? ПРимерно так (возможны опечатки — не проверял)



J>
J>let ids = [1;2;3;4]
J>let results = ids 
J>|> List.map (fun id -> async {
J>...
J>})
J>|> List.map Async.Catch
J>|> Async.Parallel 
J>|> Async.RunSynchronously
J>|> List.ofArray
 
J>let errors = results |> List.choose (function
J>                                     | Choice2Of2 e -> Some e
J>                                     | _ -> None results)
J>

все дело в том, что мне надо знать какой id вызвал ошибку.
Но за совет спасибо.
Re[4]: [F#] Запуск асинхронных задач с обработкой ошибок
От: cadet354 Россия
Дата: 16.06.10 14:18
Оценка:
Здравствуйте, achmed, Вы писали:

A>Здравствуйте, cadet354, Вы писали:


C>>Здравствуйте, achmed, Вы писали:


A>>>1) Почему бы не использовать try внутри async?

C>>не кошерно
A>даже не знаю что сказать ...
видно придется именно так и сделать.

A>>>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?

C>>а как получить список ошибок?
A>Parallel вернет список Choice<(string*string),exn>
а id вызвавшего ошибку?
Re[5]: [F#] Запуск асинхронных задач с обработкой ошибок
От: WolfHound  
Дата: 16.06.10 14:32
Оценка:
Здравствуйте, cadet354, Вы писали:

C>все дело в том, что мне надо знать какой id вызвал ошибку.

C>Но за совет спасибо.
На немерле можно сделать так
list.Map((id, url) => (id, HttpGet(url).Start())).Iter((id, result) =>
{
match (result.GetResult())
{
| Value(value) => ...
| Exception(ex) => ...
| Canceled => ...
}
});
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: [F#] Запуск асинхронных задач с обработкой ошибок
От: Jack128  
Дата: 16.06.10 15:36
Оценка:
Здравствуйте, cadet354, Вы писали:

C>все дело в том, что мне надо знать какой id вызвал ошибку.

C>Но за совет спасибо.

ну так сделай try with внутри async'а и не парься.

async {
  try
    ...
    return Choice1Of2 (id, result)
  with
    | exn e -> return Choice2Of2 (id, e)
}
Re[2]: [F#] Запуск асинхронных задач с обработкой ошибок
От: Jack128  
Дата: 16.06.10 15:47
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, cadet354, Вы писали:


C>>Как быть в этой ситуации?

WH>Использовать правильные языки.

На F# можно тоже самое...
Re[3]: [F#] Запуск асинхронных задач с обработкой ошибок
От: WolfHound  
Дата: 16.06.10 16:21
Оценка:
Здравствуйте, Jack128, Вы писали:

J>На F# можно тоже самое...

Версия F# много чего не умеет.
Например она не может так:
http://code.google.com/p/nemerle/source/browse/nemerle/trunk/snippets/ComputationExpressions/WindowsFormsTest/MainForm.n
Причем просто по тому что это не предусмотренно архитектурой.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: [F#] Запуск асинхронных задач с обработкой ошибок
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.06.10 17:34
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, Jack128, Вы писали:


J>>На F# можно тоже самое...

WH>Версия F# много чего не умеет.
WH>Например она не может так:
WH>http://code.google.com/p/nemerle/source/browse/nemerle/trunk/snippets/ComputationExpressions/WindowsFormsTest/MainForm.n
WH>Причем просто по тому что это не предусмотренно архитектурой.

Что именно не предусмотрено?
Re[5]: [F#] Запуск асинхронных задач с обработкой ошибок
От: WolfHound  
Дата: 16.06.10 17:57
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Что именно не предусмотрено?

Выделенное
        comp async
        {
          callcomp Async.SwitchTo(poolCtx);
          def fib(n)
          {
            if (n < 2)
              1;
            else
              fib(n - 2) + fib(n - 1);
          }
          def res = fib(cur);

          callcomp Async.SwitchTo(guiCtx);
          textBox1.Text = $"fib($cur) = $res\n" + textBox1.Text;
          progressBar1.Maximum = max;
          progressBar1.Value = cur;
          when (cur < max)
            callcomp proc(cur + 1, max);
        }
      }

Они просто не предусмотрели возможность нескольких контекстов исполнения своих Async'ов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: [F#] Запуск асинхронных задач с обработкой ошибок
От: Jack128  
Дата: 16.06.10 18:05
Оценка: 1 (1)
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, gandjustas, Вы писали:


G>>Что именно не предусмотрено?

WH>Выделенное
WH>
WH>        comp async
WH>        {
WH>          callcomp Async.SwitchTo(poolCtx);
WH>          def fib(n)
WH>          {
WH>            if (n < 2)
WH>              1;
WH>            else
WH>              fib(n - 2) + fib(n - 1);
WH>          }
WH>          def res = fib(cur);

WH>          callcomp Async.SwitchTo(guiCtx);
WH>          textBox1.Text = $"fib($cur) = $res\n" + textBox1.Text;
WH>          progressBar1.Maximum = max;
WH>          progressBar1.Value = cur;
WH>          when (cur < max)
WH>            callcomp proc(cur + 1, max);
WH>        }
WH>      }
WH>

WH>Они просто не предусмотрели возможность нескольких контекстов исполнения своих Async'ов.

ну настолько я async не побывал, но беглое гугление дало такой код:

open System
open System.Threading

let print fmt = 
    let th = Thread.CurrentThread
    Printf.kprintf (printfn "%O | Id=%d, IsTP=%b, IsBg=%b|%s" DateTime.Now th.ManagedThreadId th.IsThreadPoolThread th.IsBackground) fmt

let workflow = async    {
    print "A"
    do! Async.SwitchToNewThread()
    print "B"
    do! Async.SwitchToThreadPool()
    print "C"
    do! Async.SwitchToContext testSyncContext
    print "D"
    }
Async.StartImmediate workflow
(*
> Async.StartImmediate workflow;;
2/20/2010 9:03:24 PM | Id=1, IsTP=false, IsBg=false|A
2/20/2010 9:03:24 PM | Id=3, IsTP=false, IsBg=true|B // new background thread
2/20/2010 9:03:24 PM | Id=8, IsTP=true, IsBg=true|C // thread from threadpool
2/20/2010 9:03:24 PM | Id=8, IsTP=true, IsBg=true|SynchronizationContext.Post
2/20/2010 9:03:24 PM | Id=8, IsTP=true, IsBg=true|D // post to syncContext
*)
Re[6]: [F#] Запуск асинхронных задач с обработкой ошибок
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.06.10 18:10
Оценка: 1 (1)
Здравствуйте, WolfHound, Вы писали:

WH>Они просто не предусмотрели возможность нескольких контекстов исполнения своих Async'ов.

Уверен?
Async.SwitchToContext
А еще есть
Async.SwitchToNewThread
Async.SwitchToThreadPool
Re[5]: [F#] Запуск асинхронных задач с обработкой ошибок
От: k.o. Россия  
Дата: 17.06.10 10:27
Оценка:
Здравствуйте, cadet354, Вы писали:

C>Здравствуйте, Jack128, Вы писали:


J>>Здравствуйте, cadet354, Вы писали:


C>>>Здравствуйте, achmed, Вы писали:


A>>>>2) Почему бы не подготовить список в котором лежат элементы типра Async<Choice<(string*string),exn>> и запустить через Parallel ?

C>>>а как получить список ошибок?

J>>не понял, в чем проблема то? ПРимерно так (возможны опечатки — не проверял)


C>все дело в том, что мне надо знать какой id вызвал ошибку.


Так почему бы не прицепить id к самому Async вместо его значения?
let second f = fun (x, y) -> x, f y
let uncurry f = fun (x, y) -> f x y

let ids = [1..4]
let results =
    ids |>
    List.map (fun id -> id, async { ... }) |>
    List.unzip |>
    (second
        (Async.Catch |> List.map >>
        Async.Parallel >>
        Async.RunSynchronously >>
        List.ofArray)) |>
    (uncurry List.zip) |>
    List.map
        (function
         | id, Choice1Of2 result -> (id, result)
         | id, Choice2Of2 (ex : exn) -> (id, ex.Message))
Re[6]: [F#] Запуск асинхронных задач с обработкой ошибок
От: cadet354 Россия
Дата: 17.06.10 10:50
Оценка:
Здравствуйте, k.o., Вы писали:


C>>все дело в том, что мне надо знать какой id вызвал ошибку.


KO>Так почему бы не прицепить id к самому Async вместо его значения?

KO>
KO>let second f = fun (x, y) -> x, f y
KO>let uncurry f = fun (x, y) -> f x y

KO>let ids = [1..4]
KO>let results =
KO>    ids |>
KO>    List.map (fun id -> id, async { ... }) |>
KO>    List.unzip |>
KO>    (second
KO>        (Async.Catch |> List.map >>
KO>        Async.Parallel >>
KO>        Async.RunSynchronously >>
KO>        List.ofArray)) |>
KO>    (uncurry List.zip) |>
KO>    List.map
KO>        (function
KO>         | id, Choice1Of2 result -> (id, result)
KO>         | id, Choice2Of2 (ex : exn) -> (id, ex.Message))
KO>

честно говоря это слишком сложно для меня (даже прочитать с N-попытки)
Re[7]: [F#] Запуск асинхронных задач с обработкой ошибок
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.06.10 11:19
Оценка:
Здравствуйте, cadet354, Вы писали:

C>честно говоря это слишком сложно для меня (даже прочитать с N-попытки)


Тогда просто используй try catch
Re[7]: [IMG 37kb] [F#] Запуск асинхронных задач с обработкой
От: k.o. Россия  
Дата: 17.06.10 11:41
Оценка: 8 (2)
Здравствуйте, cadet354, Вы писали:

C>Здравствуйте, k.o., Вы писали:



C>>>все дело в том, что мне надо знать какой id вызвал ошибку.


KO>>Так почему бы не прицепить id к самому Async вместо его значения?

C>честно говоря это слишком сложно для меня (даже прочитать с N-попытки)

На самом деле это почти не отличается от того, что предлагал
Автор: Jack128
Дата: 16.06.10
Jack128. Работает оно примерно вот так:

Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.