Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Могут ли возникнуть ошибки одновременного доступа к объекту в следующем коде?
Код выполняется в Silverlight.

Есть некий сервис, запрашивающий данные:
public static class DataService{
	public static void GetSomeData(long id, Action<SomeData> callback){
		var client = new WebClient();
		client.DownloadStringAsync(uri);
		client.DownloadStringCompleted+=(s,ea)=>{
			if (ea.error != null)
				callback(ea.Result)
			else
				MessageBox.show(ea.error);
		}
	}
}
И есть код:
pulbic void Test(){
	var asyncCounter = 0;
	foreach (var id in idList){
		asyncCounter++;
		DataService.GetSomeData(id, data=>{
			asyncCounter--;
			if (asyncCounter == 0){
				DoSmthElse(...);
			}
		});
	}
}
Здесь происходит несколько одновременных запросов, по завершении которых происходит вызов DoSmthElse();
Достигается это за счет использования переменной asyncCounter.

Будут ли проблемы при использовании этой одной переменной в обработчиках разных запросов в Silverlight?
21 май 12, 11:56    [12587307]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
AlexeiK
Member

Откуда:
Сообщений: 2859
enigmatic,

предлагаешь всем подебажить твои отрывки кода в своих головах? тогда тебе в тему "Экстрасенсы говорят".
21 май 12, 12:12    [12587448]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Будут ли проблемы при использовании этой одной переменной в обработчиках разных запросов в Silverlight?
Да.

зы: Если DownloadStringCompleted выполняется в гуйном потоке - тогда нет. Надо смотреть мануал по этому событию.
21 май 12, 12:38    [12587693]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
зы: Зря ты не послушал совета, и не стал использовать Task-и. Вместо этой байды применил бы Task.ContinueWhenAll. Дело твоё конечно, просто мысль...
21 май 12, 12:41    [12587717]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К
enigmatic
Будут ли проблемы при использовании этой одной переменной в обработчиках разных запросов в Silverlight?
Да.

зы: Если DownloadStringCompleted выполняется в гуйном потоке - тогда нет. Надо смотреть мануал по этому событию.

Хорошо. Значит, можно (пока) не беспокоиться.
http://msdn.microsoft.com/en-us/library/cc197953(v=vs.95).aspx
The WebClient callback, which is raised when the HTTP response is returned, is invoked on the User Interface (UI) thread, and can be used to update the properties of UI elements.

Алексей К
зы: Зря ты не послушал совета, и не стал использовать Task-и. Вместо этой байды применил бы Task.ContinueWhenAll. Дело твоё конечно, просто мысль...
Пока просто не могу этого сделать, т.к. пока не умею с тасками и пока в приоритете работа вообще не связанная с этим топиком.
Целью этого вопроса было убедиться, что вышеописанное не таит в себе скрытых граблей, на которые можно нарваться по неопытности.
В перспективе, нужно будет реализовать то же самое на Task (или Rx).

AlexeiK
предлагаешь всем подебажить твои отрывки кода в своих головах? тогда тебе в тему "Экстрасенсы говорят".
Если хочется подебажить, то можно сделать тестовый солюшен с сервисом и обращением. Сделать?
21 май 12, 14:30    [12588789]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Попробовал через Task.ContinueWhenAll. Все отработало замечательно.
Прилагаю тестовый проект под 5-й SL.

Как бы сделать подобное под четвертым, где нет Task'ов?
Обновиться до пятого возможности пока нет.

Попробовал Rx под четвертым. Не получилось. Думаю, Rx предназначен скорее для работы с массивами данных генерируемыми мышкой, нежели с "действиями". Возможно не прав.

P.S. В тестовом проекте есть интересный момент - обработчики WebClient.DownloadStringCompleted отрабатывают после параметра Action<Tasks[]> continuationAction метода Task.Factory.ContinueWhenAll, хотя сначала выполняется код методов Get. Поэтому сначала выводится сообщение "Complete", а затем пара "Got 1-й/2-й".

К сообщению приложен файл (testTplTask.rar - 20Kb) cкачать
13 июн 12, 12:53    [12707024]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
enigmatic
В тестовом проекте есть интересный момент ...

Все правильно. Сначала выполняется код тасков, затем завершающий метод (continuationAction) и потом обработчики DownloadStringCompleted. Потому как continuationAction не будет ожидать обработчиков без соответствующей обработки.
14 июн 12, 06:35    [12711138]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Все правильно.
Всё не правильно. :-)

Вместо
        private void Get(Uri uri)
        {
            var client = new System.Net.WebClient();
            client.DownloadStringAsync(uri);
            client.DownloadStringCompleted += (s, ea) => Function(uri.ToString());
        }

должно быть что-то вроде
var t1 = Get(uri1);
var t2 = Get(uri2);

Task.Factory.ContinueWhenAll(new[] { t1, t2 }, tt => Dispatcher.BeginInvoke(() => MessageBox.Show("Complete")));

...

private Task Get(Uri uri)
{
    return Task.Factory.FromAsync(........);
}


зы: для синхронизации вместо явного вызова Dispatcher.BeginInvoke можно использовать контекст синхронизации.
14 июн 12, 07:25    [12711165]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К
Всё не правильно. :-)
Тот пример был написан на скорую руку и необдуманно. :)

Попробовал пример обертки EAP (Event-based Async Pattern) с MSDN (линк (раздел Exposing Complex EAP Operations As Tasks) и линк). Под нормальным дотнетом работает, под SL - виснет. Видимо, под SL опять тонкости, связанные с UI-потоком.
Прилагаю тестовые проекты под раром, они оба используют один и тот же "AsyncService"-класс для обертки EAP в таск:
ConsoleApplication1 - нормальный дотнет,
testTplTask - сильверлайт.
Если пробовать дебажить, то лучше начать с SL-проекта, чтобы поднять локальный вебсервер, на который будет потом коннектиться нормальный дотнет.
Алексей К
Вместо
        private void Get(Uri uri)
        {
            var client = new System.Net.WebClient();
            client.DownloadStringAsync(uri);
            client.DownloadStringCompleted += (s, ea) => Function(uri.ToString());
        }
должно быть что-то вроде
var t1 = Get(uri1);
var t2 = Get(uri2);

Task.Factory.ContinueWhenAll(new[] { t1, t2 }, tt => Dispatcher.BeginInvoke(() => MessageBox.Show("Complete")));

...

private Task Get(Uri uri)
{
    return Task.Factory.FromAsync(........);
}
Спасибо, попробую.
Алексей К
зы: для синхронизации вместо явного вызова Dispatcher.BeginInvoke можно использовать контекст синхронизации.
Тоже попробую.
14 июн 12, 08:17    [12711221]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
enigmatic,

автор
Прилагаю тестовые проекты под раром,


К сообщению приложен файл (testProjects.rar - 26Kb) cкачать
14 июн 12, 09:42    [12711573]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К
должно быть что-то вроде
var t1 = Get(uri1);
var t2 = Get(uri2);

Task.Factory.ContinueWhenAll(new[] { t1, t2 }, tt => Dispatcher.BeginInvoke(() => MessageBox.Show("Complete")));

...

private Task Get(Uri uri)
{
    return Task.Factory.FromAsync(........);
}

Похоже, Task.Factory.FromAsync(...) применяется для оборачивания таких методов как HttpWebRequest.BeginResponse/EndResponse (которые Async Programming Model), но не WebClient.DownloadStringAsync/DownloadStringCompleted (которые Event-based Async Pattern). По крайней мере, применить FromAsync к DownloadStringCompleted не получилось.
Для обертки EAP, судя по всему, предназначен TaskCompletionSource.
Но его использование под SL, похоже, вешает UI поток. Причем, похоже, TaskCompletionSource вешает его только под SL, но не в обычном .NET.
Гуглинг по использованию FromAsync для WebClient.DownloadStringCompleted не помог найти ничего нужного, но вместо этого дал отсылку к TaskCompletionSource (секция Converting an Event-Based Pattern).
14 июн 12, 13:06    [12713170]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К,

Не приходилось ли оборачивать WebClient с его DownloadStringAsync/DownloadStringCompleted в Task используя Task.Factory.FromAsync(...)?
14 июн 12, 13:09    [12713190]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Похоже, Task.Factory.FromAsync(...) применяется для оборачивания таких методов как HttpWebRequest.BeginResponse/EndResponse (которые Async Programming Model), но не WebClient.DownloadStringAsync/DownloadStringCompleted (которые Event-based Async Pattern). По крайней мере, применить FromAsync к DownloadStringCompleted не получилось.
Согласен. Может как-то и можно, но у меня тоже не получилось. :-)

С другой стороны через TaskCompletionSource всё просто. Не запускал, но вроде должно работать:
    public static class WebClientHelper
    {
        public static Task<string> DownloadStringAsyncEx(this WebClient wc, Uri uri)
        {
            var tcs = new TaskCompletionSource<string>();
            wc.DownloadStringCompleted += DownloadStringCompleted;
            wc.DownloadStringAsync(uri, tcs);
            return tcs.Task;    
        }

        static void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            var wc = (WebClient)sender;
            wc.DownloadStringCompleted -= DownloadStringCompleted;

            var tcs = (TaskCompletionSource<string>)e.UserState;

            if (e.Cancelled)
            {
                tcs.SetCanceled();
                return;
            }

            if (e.Error != null)
            {
                tcs.SetException(e.Error);
                return;
            }

            tcs.SetResult(e.Result);
        }
    }

В случае с Силверлайтом - не знаю, надо думать, у нас тут жара аж капец... :-)
14 июн 12, 13:29    [12713361]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Почему бы не использовать HttpWebRequest.BeginGetResponse/EndGetResponse?
14 июн 12, 13:46    [12713548]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К
С другой стороны через TaskCompletionSource всё просто. Не запускал, но вроде должно работать:
    public static class WebClientHelper
    {
        public static Task<string> DownloadStringAsyncEx(this WebClient wc, Uri uri)
        {
            var tcs = new TaskCompletionSource<string>();
            wc.DownloadStringCompleted += DownloadStringCompleted;
            wc.DownloadStringAsync(uri, tcs);
            return tcs.Task;    
        }

        static void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            var wc = (WebClient)sender;
            wc.DownloadStringCompleted -= DownloadStringCompleted;

            var tcs = (TaskCompletionSource<string>)e.UserState;

            if (e.Cancelled)
            {
                tcs.SetCanceled();
                return;
            }

            if (e.Error != null)
            {
                tcs.SetException(e.Error);
                return;
            }

            tcs.SetResult(e.Result);
        }
    }

Хм ... :) Использовать TaskCompletionSource в методе возврата Task. Хорошая мысль. Даже как-то не подумалось о такой возможности.

Алексей К
В случае с Силверлайтом - не знаю, надо думать, у нас тут жара аж капец... :-)
Попробовал, благополучно виснет, причем только под SL.
То ли TaskCompleteSource вешает UI, то ли я что-то совсем не так делаю :)
Мы тоже кондиционеры на полную выкручиваем.

Алексей К
Почему бы не использовать HttpWebRequest.BeginGetResponse/EndGetResponse?
Надо будет попробовать. Но подгонять задачу (расправиться с WebClient'ом) под средства (Task'и) не наш метод же!

К сообщению приложен файл (testProjects.rar - 23Kb) cкачать
14 июн 12, 14:14    [12713780]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
enigmatic
Попробовал, благополучно виснет, причем только под SL...
Тестовый код сейчас выглядит примерно так:
public partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();

            var task = DownloadStringAsyncEx(new Uri("http://localhost:1732/data1.txt"));

            MessageBox.Show(string.Format("Got {0}", task.Result));
        }

        public Task<string> DownloadStringAsyncEx(Uri uri) {
            var tcs = new TaskCompletionSource<string>();
            var client = new WebClient();
            client.DownloadStringCompleted += (s, ea) => tcs.SetResult(ea.Result);
            client.DownloadStringAsync(uri, tcs);
            return tcs.Task;
        }
    }
14 июн 12, 14:18    [12713828]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Но подгонять задачу (расправиться с WebClient'ом) под средства (Task'и) не наш метод же!
Боюсь, ничего хорошего из этого не выйдет. Мне кажется, WebClient с его встроенной синхронизацией и Task-и вещи взаимоисключающие.
14 июн 12, 15:01    [12714134]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Тестовый код сейчас выглядит примерно так:
    public partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();
            var task = DownloadStringAsyncEx(new Uri("http://localhost:1732/data1.txt"));
            MessageBox.Show(string.Format("Got {0}", task.Result));
        }

Так конечно, правильно что виснет. :-)
Надо как-то так:
    public partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();
            
            DownloadStringAsyncEx(new Uri("http://localhost:1732/data1.txt"))
                .ContinueWith(t => Dispatcher.BeginInvoke(() =>
                       MessageBox.Show(string.Format("Got {0}", t.Result)
                )); // + обработку ошибок добавить
        }
14 июн 12, 15:25    [12714277]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
enigmatic
Member

Откуда:
Сообщений: 729
Алексей К,

Но ведь консольное приложение отрабатывает нормально. Уверен, проблемы из-за отличия CLR SL от CLR обычного дотнета.
14 июн 12, 15:35    [12714352]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
enigmatic
Но ведь консольное приложение отрабатывает нормально. Уверен, проблемы из-за отличия CLR SL от CLR обычного дотнета.
Нет. Отличие в том, что приложение консольное. А в силверлайтном приложении возникает мёртвая блокировка. Ожидание завершения при обращении к свойству task.Result в конструкторе в гуйном потоке + событие завершения идёт в гуйном потоке == мёртвая блокировка.
15 июн 12, 05:52    [12716980]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
Все отличия из-за того, что в Силверлайте DownloadStringCompleted синхронизирован с гуйным потоком. Но эти отличия были бы видны при сравнении с например WPF-ным приложением.
15 июн 12, 05:54    [12716981]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
enigmatic
Могут ли возникнуть ошибки одновременного доступа к объекту в следующем коде?
Код выполняется в Silverlight.

Есть некий сервис, запрашивающий данные:
public static class DataService{
	public static void GetSomeData(long id, Action<SomeData> callback){
		var client = new WebClient();
		client.DownloadStringAsync(uri);
		client.DownloadStringCompleted+=(s,ea)=>{
			if (ea.error != null)
				callback(ea.Result)
			else
				MessageBox.show(ea.error);
		}
	}
}
И есть код:
pulbic void Test(){
	var asyncCounter = 0;
	foreach (var id in idList){
		asyncCounter++;
		DataService.GetSomeData(id, data=>{
			asyncCounter--;
			if (asyncCounter == 0){
				DoSmthElse(...);
			}
		});
	}
}
Здесь происходит несколько одновременных запросов, по завершении которых происходит вызов DoSmthElse();
Достигается это за счет использования переменной asyncCounter.

Будут ли проблемы при использовании этой одной переменной в обработчиках разных запросов в Silverlight?


Могут и будут. Асинхронные вызовы могут выполняться в разной последовательности, вызовы нужно делать с дополнительным параметром state, который нужно анализировать в обработчике. Помимо этого есть утечки памяти. При множественных вызовах webrequest - более предпочтительных вариант, тк нет лишней диспетчеризации в ui поток.
16 июн 12, 11:06    [12724093]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
Алексей К
Все отличия из-за того, что в Силверлайте DownloadStringCompleted синхронизирован с гуйным потоком. Но эти отличия были бы видны при сравнении с например WPF-ным приложением.


Привильно здесь deadlock, а task'и - унылый гумос. rx в разы удобней и лучше.В особенности для клиентских приложений
16 июн 12, 11:10    [12724097]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
Алексей К
Member

Откуда: Новосибирск
Сообщений: 13632
SeVa
а task'и - унылый гумос. rx в разы удобней и лучше.В особенности для клиентских приложений
Для построения цепочки асинхронных операций таски удобны. Всё просто и ничего лишнего.

Покажи как будет выглядеть аналогичный код на Rx.
Task<int> F1()
{ return ... }

Task<int> F2(int v1)
{ return ... }

Task<int> F3(int v2)
{ return ... }

...


Task<int> Execute()
{
    return F1()
        .ContinueWith(t => F2(t.Result))
        .Unwrap()
        .ContinueWith(t => F3(t.Result))
        .Unwrap();
}
16 июн 12, 12:07    [12724196]     Ответить | Цитировать Сообщить модератору
 Re: [SL] Одна переменная для нескольких обработчиков  [new]
SeVa
Member [заблокирован]

Откуда: Москва
Сообщений: 4324
Элементарно
var res = from t1 in Observable.Return(Task1())
              from t2 in Observable.Return(Task2())
              select t1+t2;
or
    Observable.Return(Task1()).And(Observable.Return(Task2()).Then((t1,t2) = > t1+t2)      


Пример и вопрос примитивный, тк task'и только под это заточены, а у rx возможностей больше
16 июн 12, 12:44    [12724263]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / WPF, Silverlight Ответить