Given: An extension method taking a Selenium IWebdriver instance and returning an IObservable
public static IObservable<ObservableCollection<WebElementWrapper>>
GetAllElementsAsObservable(this IWebDriver wd)
{
return Observable.Create<ObservableCollection<WebElementWrapper>>(
(IObserver<ObservableCollection<WebElementWrapper>> observer) =>
{
var eles = wd.FindElements(By.CssSelector("*"));
var list = eles.ToWebElementObservableCollection();
observer.OnNext(list);
observer.OnCompleted();
return Disposable.Create(() => { });
});
}
And the code that calls the method above (running on GUI thread)...
//GUI Will Freeze on this call until OnCompleted is called
cd.GetAllElementsAsObservable().Subscribe((WEWList) =>
{
WebElementCollection = WEWList;
SetNavigationItems();
});
Can anyone help me determine root cause of GUI thread block until OnCompleted is called. I can stop the blocking if I use Task.Run in first method, but then I have to marshall the collection back onto GUI thread.
Does this block because the GUI thread spun up the Webdriver of which the Observable is using to extract elements?
Or is this due to the static methods being created at start up time on the GUI thread?
If ever you do this - Disposable.Create(() => { }) - you are doing something wrong. Using Observable.Create the way you are using it is a blocking operation. The code inside the .Create is part of the subscription, but you're running the observer to completion during subscription which is why it is blocking.
Try doing something like this instead:
public static IObservable<ObservableCollection<WebElementWrapper>>
GetAllElementsAsObservable(this IWebDriver wd)
{
return Observable.Create<ObservableCollection<WebElementWrapper>>(observer =>
Observable
.Start(() =>
wd
.FindElements(By.CssSelector("*"))
.ToWebElementObservableCollection())
.Subscribe(observer));
}
For WPF, I've also found these two methods to work..
SomeObservable
.SubscribeOn(Scheduler.Default)
.ObserveOn(Scheduler.CurrentThread)
.Subscribe(item => { //do something on gui thread here });
I don't care for the method name SubscribeOn, but I look at it this way... I want the observable to SubscribeOn some scheduler. (I think a better name would have been "SheduleOn").
The ObserveOn method name makes sense. But note the "Scheduler.Dispatcher" built-in property.
Related
In my MVVM application, when a ViewModel gets activated, a Task gets started that establishes a network connection and could take some time to complete. This Task is cancalable:
private async Task ConnectAsync(CancellationToken cancellationToken = default)
{
...
}
I'm using IActivatableViewModel to start it on ViewModel-activation like that:
// Constructor:
public SomeViewModel(...)
{
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync);
});
}
Now what is the recommended method to cancel this long-running Task when the ViewModel gets deactivated before the Task completes?
I came up with this:
this.WhenActivated(disposable => {
Observable.StartAsync(ConnectAsync).Subscribe().DisposeWith(disposable);
});
Is this the right solution or is there a better one?
Thank you in advance!
Yeah, the code like you show in your code snippet looks good. However, probably worth moving the ConnectAsync method call to a ReactiveCommand<TInput, TOutput> (docs). If you do this, you get such perks as the ability to subscribe to ThrownExceptions and IsExecuting observables, and then display some loading indicators or error messages to keep your users informed about what the app is doing. Also, following the pattern described here, you can cancel that ReactiveCommand<TInput, TOutput> via another command or event. Cancelation via an event would look like this:
// ViewModel.cs
Cancel = ReactiveCommand.Create(() => { });
Connect = ReactiveCommand
.CreateFromObservable(
() => Observable
.StartAsync(ConnectAsync)
.TakeUntil(Cancel));
// View.xaml.cs
this.WhenActivated(disposable => {
this.Events() // Launch the long-running operation
.Loaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Connect)
.DisposeWith(disposable);
this.Events() // Stop that long-running operation
.Unloaded
.Select(args => Unit.Default)
.InvokeCommand(ViewModel, x => x.Cancel)
.DisposeWith(disposable);
});
Here, I assume ConnectAsync is a method accepting a cancelation token and returning a Task. In order to enable the this.Events() magic, you need to either use Pharmacist, or to install one of the ReactiveUI.Events packages. But anyway, your option looks good as well if you want to rely on WhenActivated, don't need ThrownExceptions, IsExecuting etc. If you'd like to use commands and rely on WhenActivated, then modify the View.xaml.cs code:
// View.xaml.cs
this.WhenActivated(disposable => {
Connect.Execute().Subscribe();
Disposable
.Create(() => Cancel.Execute().Subscribe())
.DisposeWith(disposable);
});
We aren't disposing of the subscriptions returned by Execute() because they'll get disposed anyway when the commands complete their execution. Hope this helps! ✨
I have a constructor that call Task.Run() like this:
public MyPage() {
Task.Run(() => {
MyHeavyCpuMethod();
});
}
Here, MyPage() is the constructor of a UI component, and I don't want MyHeavyCpuMethod() to run on my UI thread, so I offload it with Task.Run() in a fire-and-forget fashion since I don't really care when MyHeavyCpuMethod() finishes.
However, this way if MyHeavyCpuMethod() throws, I can't handle the exception that is in the returned Task.
How can I do error handling in this case?
One option is to use async/await... which doesn't work with a constructor, but which can work in a static method:
public static async Task<MyPage> CreateInstance()
{
await Task.Run(...);
// Anything else asynchronous you want to use
return new MyPage();
}
And then assuming you're using this from an async method, you can just use:
MyPage page = await MyPage.CreateInstance();
That way, if the CPU-bound task fails, you won't even get to the constructor call. The constructor call itself is expected to be fast here, as that will be on the UI thread (as you want it to be).
An alternative to this, you could potentially store the task returned by Task.Run as a field in the page, and then await that post-construction... using the normal async exception handling approaches.
Add a ContinueWith that only fires when the Task doesn't run to completion (throws an exception):
Task.Run(() => MyHeavyCpuMethod())
.ContinueWith(task => { /* some action */ }, TaskContinuationOptions.OnlyOnFaulted);
Your second task will not be run on the UI thread either, per the documentation:
Creates a continuation that executes asynchronously when the target Task completes.
You can play with a dummy method to try it out:
Task.Run(() =>
{
throw new Exception();
}).ContinueWith(t =>
{
Invoke((MethodInvoker)(() => MessageBox.Show("ERROR")));
}, TaskContinuationOptions.OnlyOnFaulted);
How could I call the onCompleteCallBack method on the same thread the SomeAsyncMethod was called ?
public void SomeAsycMethod ( Action<object> onCompleteCallBack )
{
// get the current thread
/* var ThisThread = Thread.CurrentThread. */
Task.Factory.StartNew( () =>
{
Thread.Sleep( 1000 );// do some work;
// lastly call the onCompleteCallBack on 'ThisThread'
onCompleteCallBack( "some result" );
// I am looking for something like:
/* ThisThread.Invoke("some result"); */
});
}
While you can't guarantee you callback will be called on the same thread, you can guarantee it will be called in the same Synchronization Context (assuming one exists in the original call).
public void SomeAsycMethod ( Action<object> onCompleteCallBack )
{
// get the current context
var context = SynchronizationContext.Current;
Task.Factory.StartNew( () =>
{
Thread.Sleep( 1000 );// do some work;
// lastly call the onCompleteCallBack on 'ThisThread'
onCompleteCallBack( "some result" );
// I am looking for something like:
context.Post(s => onCompleteCallBack ("some result"), null);
});
}
For example, in a Windows Forms or WPF program, the above will make sure that the callback is called on the GUI thread (via the message loop or dispatcher, accordingly). Similarly for ASP.NET context.
Having said that, I agree with Justin Harvey in that returning a Task<T> will probably be a better design.
Actually if you're using Task-based asynchronous programming I suggested you refactor your code to return Task<T> and give an ability to your client itself to decide in what context to call callback method (and facilitate future migration to C# 5.0 ;):
public Task<string> SomeMethodAsync()
{
return Task.Factory.StartNew(() => "some result");
}
If you definitely know that you're going to call this method from UI thread you can use following:
var task = SomeMethodAsync();
task.ContinueWith(t => textBox.Text = t.Result, TaskScheduler.FromSynchronizationContext);
This approach is better because it provide more clear separation of concern and give an ability to use your asynchronous method in any context without any dependencies to synchronization context. Some client can call this method from UI thread (and in this case TaskScheduler.FromSynchronizationContext would behave as expected - your "continuation" would be called in UI thread), some of them could use your method from non-UI thread as well without such requirements like processing results in the same thread that initiate asynchronous operation.
Task<T> is a perfect class that represents asynchronous operation as a first class object that helps not only obtain only more declarative code but more clear, easy to read and easy to test (you can easily mock this method and return "fake" task object).
I am looking for a simple way to do a task in background, then update something (on the main thread) when it completes. It's in a low level 'model' class so I can't call InvokeOnMainThread as I don't have an NSObject around. I have this method:
public void GetItemsAsync(Action<Item[]> handleResult)
{
Item[] items=null;
Task task=Task.Factory.StartNew(() =>
{
items=this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(items);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
This seems to work OK, but
1) Is this the best (simplest) way?
2) I'm worried about the local variable:
Item{} items=null
What stops that disappearing when the method returns before the background thread completes?
Thanks.
I think your method slightly violates a Single Responsibility Principle, because it doing too much.
First of all I suggest to change CreateItems to return Task instead of wrapping it in the GetItemsAsync:
public Task<Item[]> CreateItems(CancellationToken token)
{
return Task.Factory.StartNew(() =>
// obtaining the data...
{});
}
CancellationToken is optional but can help you if you able to cancel this long running operation.
With this method you can remove GetItemsAsync entirely because its so simple to handle results by your client without passing this delegate:
// Somewhere in the client of your class
var task = yourClass.CreateItems(token);
task.ContinueWith(t =>
// Code of the delegate that previously
// passed to GetItemsAsync method
{}, TaskScheduler.FromCurrentSynchronizationContext());
Using this approach you'll get more clear code with only one responsibility. Task class itself is a perfect tool for representing asynchronous operation as a first class object. Using proposed technique you can easily mock you current implementation with a fake behavior for unit testing without changing your clients code.
Something like this:
public void GetItemsAsync(Action<Item[]> handleResult)
{
int Id = 11;
Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two
task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
Your code looks fine.
It's a perfect example of when to use async / await if you can use C# 5, but if not, you have to write it as you have done with continuations.
The items variable is captured in your lambdas, so that's fine too.
When you write a lambda that uses an outside variable, the C# compiler creates a class that contains the variable. This is called a closure and means you can access the variable inside your lambda.
Here is a nicer soultion:
public void GetItemsAsync(Action<Item[]> handleResult)
{
var task = Task.Factory.StartNew<Item[]>(() =>
{
return this.CreateItems(); // May take a second or two
});
task.ContinueWith(delegate
{
handleResult(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
I am new to asynchronous programming. I have a C# dll with an asynchronous method that gets called, takes a function pointer (delegate) and calls this callback function after "result" is calculated.
public delegate void CreatedDelegate(Foo result);
public void CreateAsync(CreatedDelegate createdCallback)
{
Task t = Task.Factory.StartNew(() =>
{
Foo result = ...
createdCallback(result);
});
}
The delegate callback of type "CreatedDelegate" is (in my case) a function pointer to a C++/CLI method that works with the result.
void CreatedCallback(Foo^ result)
{
// do something with result
}
So this asynchronous concept seems to work quite well in most cases, but sometimes I encounter some errors. How can I achieve it if the function "CreateAsync" is called multiple times with different computation effort, that the resulting calls to "CreatedCallback" just happen in the same order as originally "CreateAsync" was called? To make it clearer: The first call to "CreateAsync" should result in the first call to "CreatedCallback" even if a succeeding call of "CreateAsync" is faster and would actually call the callback earlier.
Maybe this can be done by allowing only one active new thread in the asynchronous "CreateAsync" at a time?
To process the callbacks in order, you'll need to implement some queueing of work items. The easiest way is probably to use BlockingCollection type (see MSDN documentation).
Instead of calling the callback, your CreateAsync method would add the task (together with the callback) to the queue:
// Queue to keep tasks and their callbacks
private BlockingCollection<Tuple<Task<Foo>, CreatedDelegate>>
queue = new BlockingCollection<Tuple<Task<Foo>, CreatedDelegate>>()
public void CreateAsync(CreatedDelegate createdCallback) {
Task<Foo> t = Task.Factory.StartNew(() => {
Foo result = ...
return result; });
queue.Add(Tuple.Create(t, createdCallback));
// ..
}
This will only add tasks and callbacks to the queue - to actually call the callback, you'll need another task that waits for the tasks in the queue (in the order in which they were added) and calls the callback:
Task.Factory.StartNew(() => {
while(true) { // while you keep calling 'CreateAsync'
// Get next task (in order) and its callback
Tuple<Task<Foo>, CreatedDelegate> op = queue.Take();
// Wait for the result and give it to callback
op.Item2(op.Item1.Result);
}
}
If order is important, then using Threads might be better:
thread queue = empty
for each task
{
if there are no free 'cpu'
wait on first thread in queue
remove thread from queue
call delegate
create thread
add thread to queue
}
while queue has threads
wait on first thread in queue
remove thread from queue
call delegate