Reactive Extensions subscribing to an observable (subject) - task-parallel-library

I'm just playing around with Reactive Extensions for the first time in a winforms application. Mind you I have been doing web development for the past 4 years, and I am very familiar with observables and observable pattern in knockout, which I am guessing is contributing to my confusion here.
Anyhow, to the question and code. I have a simple winforms experiment (see below) that I was building to illustrate my question. The subscribe below doesn't run until well after the thread in start new is finished. I can trace it the calls to OnNext, but the subscribe doesn't fire at all until sometimes 20-30 seconds later. Can somebody explain this behavior to me?
public partial class Form1 : Form
{
private Subject<int> progress;
private CancellationToken cancellationToken;
private IScheduler _scheduler;
public Form1()
{
InitializeComponent();
CancellationTokenSource source = new CancellationTokenSource();
cancellationToken = source.Token;
_scheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);
}
private void Start_Click(object sender, EventArgs e)
{
progress
.ObserveOn(_scheduler)
//.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(
(i) => {
progressBar1.Do<ProgressBar>(ctl =>
{
ctl.Value = i;
});
},
(ex) => { },
cancellationToken
);
Task counterTask = Task.Factory.StartNew(() =>
{
for (var i = 1; i < 101; i++)
{
Thread.Sleep(500);
progress.OnNext(i);
}
}, cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
private void Form1_Load(object sender, EventArgs e)
{
progress = new Subject<int>();
}
}
public static class ControlExtensions
{
public static void Do<TControl>(this TControl control, Action<TControl> action)
where TControl : Control
{
if (control.InvokeRequired)
control.Invoke(action, control);
else
action(control);
}
}

Your issue comes from the fact that your task is running on the UI thread, because you're using TaskScheduler.FromCurrentSynchronizationContext().
Hence your various Sleep calls are blocking the UI thread, freezing the UI (e.g. can't drag the window) and preventing your observable subscription to execute (because the ObserveOn, it's supposed to execute on the UI thread scheduler).
Replace TaskScheduler.FromCurrentSynchronizationContext() by TaskScheduler.Default (background TaskPool threads), and everything will work as you expected.
Note that your call to Do/Invoke is unnecessary, because you're already on the UI thread by the scheduler you've provided.

Related

Suspending(), Resuming(), Closed() and Uninitialize() are not being called in IFrameworkView

I've written a simple DirectX11.2 app, which works. I wanted to add some cleanup code for when the app exits, however I noticed that my window does not actually handle closing, suspending, resuming or uninitializing properly.
According to the IFrameworkView documentation, Uninitialize() should get called before the application exits, but it never gets called (https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.core.iframeworkview?view=winrt-19041)
I subscribe to the events that are supposed to fire when a window suspends, resumes, or closes, however it seems like none of those events ever actually fire.
I am under the impression that minimizing the window should suspend the application, clicking on the window from the task bar after it has been minimized should resume the application, and pressing the red X button in the top right corner of the window should close the application, am I wrong?
Here is the relevant code:
// the class definition for the core "framework" of our app
ref class App sealed: public IFrameworkView
{
bool m_windowClosed;
CGame m_game;
public:
// this function subscribes to suspend and resume events, and gets called properly
virtual void Initialize(CoreApplicationView^ appView) {
// set the OnActivated function to handle to Acivated "event"
appView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &App::OnActivated);
CoreApplication::Suspending += ref new EventHandler<SuspendingEventArgs^>(this, &App::Suspending);
CoreApplication::Resuming += ref new EventHandler<Object^>(this, &App::Resuming);
m_windowClosed = false;
}
// this function subscribes to the close() event. This function is called properly, but the Closed event never fires
virtual void SetWindow(CoreWindow^ window){
window->Closed += ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &App::Closed);
}
virtual void Load(String^ entryPoint) {}
virtual void Run() {
m_game.Initialize();
CoreWindow^ Window = CoreWindow::GetForCurrentThread();
// repeat until window closes
while (!m_windowClosed) {
// run processEvents() to dispatch events
// ProcessAllIfPresent makes ProcessEvents return once all events have been processed
Window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
// run the rest of the game code here
m_game.Update();
m_game.Render();
}
// we never get here!
m_game.Finalize();
}
// never called, even though it should ALWAYS be called when the application exits?
virtual void Uninitialize() {
Log("Uninitialize()");
}
void OnActivated(CoreApplicationView^ coreAppView, IActivatedEventArgs^ args) {
CoreWindow^ window = CoreWindow::GetForCurrentThread();
window->Activate();
}
// never called
void Suspending(Object^ sender, SuspendingEventArgs^ args) {
Log("Suspending()");
}
// never called
void Resuming(Object^ sender, Object^ args) {
Log("Resuming()");
}
// never called
void Closed(CoreWindow^ sender, CoreWindowEventArgs^ args) {
m_windowClosed = true;
Log("Close()");
}
};
// the class definition that creates an instance of our core framework class
ref class AppSource sealed : IFrameworkViewSource {
public:
virtual IFrameworkView^ CreateView() {
// create an App class and return it
return ref new App();
}
};
[MTAThread] // define main() as a multi-threaded-apartment function
// the starting point of all programs
int main(Array<String^>^ args) {
// create and run a new AppSource class
CoreApplication::Run(ref new AppSource());
return 0;
}
Upon further research, I notice that Suspend and Resume are generally called when Windows itself suspends (sleep, hibernate) and resumes ("wakes up" from sleep or hibernate).
I have now found that the only event that's called before my app terminates is the CoreWindow::VisibilityChanged event.

What is a practical solution to a long running program?

I have a .Net console application that is supposed to be long running and continous , basically 24 hours a day. It's for a rabbitmq consumer client. I am opening 30 channels on 1 connection, and each channel is responsible for 7 different queues.
Task creation:
tokenSource2 = new CancellationTokenSource();
cancellationToken = tokenSource2.Token;
for (int i = 0; i < 30; i++) //MAX 100 MODEL
{
List<string> partlist = tmpDBList.Take(7).ToList();
tmpDBList = tmpDBList.Except(partlist).ToList();
new Task(delegate { StartConsuming(partlist, cancellationToken); }, cancellationToken, TaskCreationOptions.LongRunning).Start();
}
The consumer method:
internal void StartConsuming(List<string> dbNames, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (IModel channel = Consumer.CreateModel())
{
foreach (string item in dbNames)
{
//Queue creation, exchange declare, bind, + basic eventhandler etc..
channel.BasicConsume(queue: item,
autoAck: true,
consumer: consumerEvent);
}
cancellationToken.ThrowIfCancellationRequested();
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.WaitHandle.WaitOne(5000);
}
}
}
Since I want the task to never stop I have the endless while cycle at the end of the using statement, otherwise the task stops, and the channels are disposed.
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.WaitHandle.WaitOne(5000);
}
Is this a an optimal solution?
Furthermore, each consumer event handler creates a DbContext of a specific database inside the
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(channel);
consumerEvent.Received += (sender, basicDeliveryEventArgs) =>
{
cancellationToken.ThrowIfCancellationRequested();
//dbContext creation
}
event handler. Will the memory be freed after the eventhandler is finished ? Do I need to Dispose of the dbcontext and each class I am using inside the eventhandler?

Easy way to stop a task from executing after certain amount of time(Xamarin.Android)

On button click I want to open a ProgressDialog which will show until a task is being executed, but I want to be able to stop the task even if it hasn't been completed after certain amount of time. I saw a lot of solutions on internet but they are very long. I want to know if there is an easier way.
here is my On Button Click event:
private async void Btn_Click(object sender, System.EventArgs e)
{
var mDialog = new ProgressDialog(this);
mDialog.SetMessage("Loading data...");
mDialog.SetCancelable(false);
mDialog.Show();
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(5000);
Task<int> task = new Task<int>(Foo, cts.Token);
task.Start();
int integer = await task;
mDialog.Dismiss();
txtView.Text = integer.ToString();
}
And here is my method which will execute in the task:
public int Foo()
{
System.Threading.Thread.Sleep(10000);
return 100;
}
Is it possible to stop the task at the 5th second of its execution without changing a lot the code I've just pasted, for example with only passing some time or object to the task's constructor, and also without making the Foo() method async
You can try
step 1. Adding a timer
Timer _timer = new Timer {Interval = 5000};
_timer.Elapsed += OnTimeEvent;
_timer.Start();
step 2.Cancel the task on timer event
private void OnTimeEvent(object sender, ElapsedEventArgs e)
{
cts .Cancel();
}

Xamarin iOS RefreshControl is stuck

I have a problem with RefreshControl... I have this code:
In ViewDidLoad() I call method InitializeRefreshControl();
private void InitializeRefreshControl()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
{
//UIRefreshControl iOS6
ordersCollectionView.RefreshControl = new UIRefreshControl();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Pull To Refresh",
new UIStringAttributes()
{
ForegroundColor = UIColor.Red,
KerningAdjustment = 3
});
ordersCollectionView.RefreshControl.ValueChanged += HandleValueChanged;
}
else
{
// old style refresh button and no PassKit for older iOS
NavigationItem.SetRightBarButtonItem(new UIBarButtonItem(UIBarButtonSystemItem.Refresh), false);
NavigationItem.RightBarButtonItem.Clicked += (sender, e) => { Refresh(); };
}
}
HandleValueChange method and Refresh merhod is here:
private void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
private void Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
viewModel.OnReloadData();
}
My problem is when I pull down collectionVIew so Refresh loading is displayed but is stuck no loading effect and still with text "Pull to refresh". When method Refresh end so for 0,1ms is showing loading effect and text "Refreshing" but not before method Refresh... Someone know how solve this problem? Thanks for answer.
It looks like the issue is related to the Refresh(); method being synchronous. You'll need to make this operation happen in the background so that the UI thread is free to provide the animation for the RefreshControl. For example:
private async void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
// await a Task so that operation is done in the background
await Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
// Marked async and Task returning
private async Task Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
// Need to update this method to be a Task returning, async method.
await viewModel.OnReloadData();
}
The above code refactors what you had to use async/await and Tasks. You may need to refactor some more of your code to make that work, including the OnReloadData() method.
There are lots of resources for getting started with Tasks, async and await. I can start you off with this reference from the Xamarin blog.

Console.ReadLine() passed to C# event

I'm learning RX and would like to use Console.ReadLine as a source for observable sequences.
I know that I can create "IEnumerable" using "yield return", but for my concrete use case I've decided to create a C# event, so that potentially many observers will be able to share the same keyboard input.
Here is my code:
class Program
{
private delegate void OnNewInputLineHandler(string line);
private static event OnNewInputLineHandler OnNewInputLineEvent = _ => {};
static void Main(string[] args)
{
Task.Run((Action) GetInput);
var input = ConsoleInput();
input.Subscribe(s=>Console.WriteLine("1: " + s));
Thread.Sleep(30000);
}
private static void GetInput()
{
while (true)
OnNewInputLineEvent(Console.ReadLine());
}
private static IObservable<string> ConsoleInput()
{
return Observable.Create<string>(
(IObserver<string> observer) =>
{
OnNewInputLineHandler h = observer.OnNext;
OnNewInputLineEvent += h;
return Disposable.Create(() => { OnNewInputLineEvent -= h; });
});
}
}
My problem - when I run the GetInput method as it is shown above, the very first input line is not sent to the sequence (but it is sent to the event handler).
However, if I replace it with the following version, everything works as expected:
private static void GetInput()
{
while (true)
{
var s = Console.ReadLine();
OnNewInputLineEvent(s);
}
}
Could someone shed some light on why this might happen?
You're trying to make life difficult for yourself. There is almost always a way to make things simple with Rx. It's just a matter of learning to think more functionally rather than procedurally.
This is all you need:
class Program
{
static void Main(string[] args)
{
var subscription = ConsoleInput().Subscribe(s => Console.WriteLine("1: " + s));
Thread.Sleep(30000);
subscription.Dispose();
}
private static IObservable<string> ConsoleInput()
{
return
Observable
.FromAsync(() => Console.In.ReadLineAsync())
.Repeat()
.Publish()
.RefCount()
.SubscribeOn(Scheduler.Default);
}
}
This lets multiple subscribers share the one input through the .Publish().RefCount(). And the .SubscribeOn(Scheduler.Default) pushes the subscription out to a new thread - without it you block on a subscription.
If you move Task.Run((Action) GetInput); to after the subscription your code will work as desired. This is because in your original version, the first call of OnNewInputEvent(Console.ReadLine()) is run before you've hooked OnNewInputLineEvent to the observer.OnNext.

Resources