How can I wait for another method to be triggered before continuing the current method while still displaying the UI - c#

I am trying to write a chess program in C# Windows Forms and I am writing a method GetMove() in this HumanPlayer class I have, which will return the Move from a player input of two clicks on separate squares on the board UI.
Could I have some help / advice on what I should use to implement this or if I am misunderstanding something else, explain that to me.
I've tried to add code snippets, but please let me know if I've done them wrong.
class HumanPlayer : Player
{
private Coords _selected;
public HumanPlayer(PieceColour colour) : base(colour)
{
_selected = new Coords();
}
public override ChessMove GetMove(Board board)
{
Coords Start = new Coords();
board.RaiseSquareClicked += ReceiveStartSquareClickInfo;
// Want to wait until that function is triggered by the event until continuing
Start = _selected;
board.RaiseSquareClicked -= ReceiveStartSquareClickInfo;
Coords End = new Coords();
board.RaiseSquareClicked += ReceiveEndSquareClickInfo;
// Want to wait until that function is triggered by the event until continuing
End = _selected;
board.RaiseSquareClicked -= ReceiveEndSquareClickInfo;
return new ChessMove(Start, End);
}
public void ReceiveStartSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_selected = e.Square.Coords;
}
public void ReceiveEndSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_selected = e.Square.Coords;
}
}
One thing I tried was using AutoResetEvent and WaitOne() and Set(), but this caused the UI to stop displaying.
I also tried to understand and use await and async, but I just confused myself and overcomplicated it and didn't get anywhere with it. So it might just be that I need someone to explain it to me.
This code that doesn't work might help someone understand what I misunderstand about asynchronous functions etc.
public async void Play()
{
_currentPlayer = _players[0];
_currentTurn = 1;
while (!GameOver())
{
ChessMove move= await _currentPlayer.GetMove(_board);
if (_currentPlayer == _players[1])
{
_currentTurn += 1;
_currentPlayer = _players[0];
}
else
{
_currentPlayer = _players[1];
}
}
}
class HumanPlayer : Player
{
private Coords _selected;
private TaskCompletionSource<bool> _squareClicked;
public HumanPlayer(PieceColour colour) : base(colour)
{
_selected = new Coords();
}
public override async Task<ChessMove> GetMove(Board board)
{
Coords Start = new Coords();
_squareClicked = new TaskCompletionSource<bool>();
board.RaiseSquareClicked += ReceiveStartSquareClickInfo;
_squareClicked.Task.Wait();
Start = _selected;
board.RaiseSquareClicked -= ReceiveStartSquareClickInfo;
Coords End = new Coords();
_squareClicked = new TaskCompletionSource<bool>();
board.RaiseSquareClicked += ReceiveEndSquareClickInfo;
_squareClicked.Task.Wait();
End = _selected;
board.RaiseSquareClicked -= ReceiveEndSquareClickInfo;
return new ChessMove(Start, End);
}
public async void ReceiveStartSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_squareClicked.SetResult(true);
_selected = e.Square.Coords;
}
public async void ReceiveEndSquareClickInfo(object sender, SquareClickedEventArgs e)
{
_squareClicked.SetResult(true);
_selected = e.Square.Coords;
}
}
I've kinda been hesitant / nervous to post this question because I don't want people to get annoyed at me for posting a "duplicate question". Even though I've looked through several of the questions, it is confusing and frustrating not knowing whether the solution just doesn't apply to my situation or if I've added it in wrong. I'm sure I could find my solution answered in another question, but I feel it would take me a lot longer to find it and understand it than posting my own question.
Sorry if I've posted this question wrong or not followed the guidelines, this is my first post here.
If I've done anything wrong in formatting / communicating through this post, let me know and I'll try to fix it.

Good question, from what I can understand, your wanting to wait on a response from another method before executing yours.
Using async/await is the best option for this, if your getting confused at that, there are some tutorials and such you can follow
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await
https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
https://dotnettutorials.net/lesson/async-and-await-operator-in-csharp/
TaskCompletionSource is a class in C# that enables creating a Task object which can be manually completed with a result or exception.
class Example
{
TaskCompletionSource<string> taskCompletionSource = new();
public async Task DoStuff()
{
// Wait for the result of the TaskCompletionSource
var result = await taskCompletionSource.Task;
Console.WriteLine(result);
}
public void SetResult(){
taskCompletionSource.SetResult("Hello World!");
}
}
In this example, calling to DoStuff method will wait until the SetResult method is called, which will then set the result variable to "Hello World!"

Your post states that you want to wait for another method to be triggered before continuing and then describes three "states of play" so my first suggestion is to identify in code exactly the things we need to wait for in the chess game loop.
enum StateOfPlay
{
PlayerChooseFrom,
PlayerChooseTo,
OpponentTurn,
}
Game Loop
The goal is to run a loop that cycles these three states continuously, waiting at each step. However, the main Form is always running its own Message Loop to detect mouse clicks and key presses and it's important not to block that loop with our own.
The await keyword causes a waiting method to return immediately which allows the UI loop to keep running. But when "something happens" that we're waiting for, the execution of this method will resume on the next line after the await. A semaphore object says when to stop or go and is initialized here in the waiting state.
SemaphoreSlim _semaphoreClick= new SemaphoreSlim(0, 1);
When the game board is clicked during the players turn then the Release() method will be called on the semaphore, allowing things to resume. In terms of the specific question that you asked, this code snippet shows how to use the await keyword in your chess game loop.
private async Task playGameAsync(PlayerColor playerColor)
{
StateOfPlay =
playerColor.Equals(PlayerColor.White) ?
StateOfPlay.PlayerChooseFrom :
StateOfPlay.OpponentTurn;
while(!_checkmate)
{
switch (StateOfPlay)
{
case StateOfPlay.PlayerChooseFrom:
await _semaphoreClick.WaitAsync();
StateOfPlay = StateOfPlay.PlayerChooseTo;
break;
case StateOfPlay.PlayerChooseTo:
await _semaphoreClick.WaitAsync();
StateOfPlay = StateOfPlay.OpponentTurn;
break;
case StateOfPlay.OpponentTurn:
await opponentMove();
StateOfPlay = StateOfPlay.PlayerChooseFrom;
break;
}
}
}
Player's turn
Here we have to wait for each square to get clicked. A straightforward way to do this is with a SemaphoreSlim object and call Release() when the game board is clicked during the player's turn.
Square _playerFrom, _playerTo, _opponentFrom, _opponentTo;
private void onSquareClicked(object sender, EventArgs e)
{
if (sender is Square square)
{
switch (StateOfPlay)
{
case StateOfPlay.OpponentTurn:
// Disabled for opponent turn
return;
case StateOfPlay.PlayerChooseFrom:
_playerFrom = square;
Text = $"Player {_playerFrom.Notation} : _";
break;
case StateOfPlay.PlayerChooseTo:
_playerTo = square;
Text = $"Player {_playerFrom.Notation} : {_playerTo.Notation}";
richTextBox.SelectionColor = Color.DarkGreen;
richTextBox.AppendText($"{_playerFrom.Notation} : {_playerTo.Notation}{Environment.NewLine}");
break;
}
_semaphoreClick.Release();
}
}
Opponents turn
This simulates a computer opponent processing an algorithm to determine its next move.
private async Task opponentMove()
{
Text = "Opponent thinking";
for (int i = 0; i < _rando.Next(5, 10); i++)
{
Text += ".";
await Task.Delay(1000);
}
string opponentMove = "xx : xx";
Text = $"Opponent Moved {opponentMove}";
richTextBox.SelectionColor = Color.DarkBlue;
richTextBox.AppendText($"{opponentMove}{Environment.NewLine}");
}
It might be helpful to look at another answer I wrote that describes how to create the game board with a TableLayoutPanel and how to interact with mouse to determine the square that's being clicked on.

Related

Await for the user to click on one control within multiple controls [duplicate]

This question already has answers here:
Is it possible to await an event instead of another async method?
(11 answers)
Closed 2 years ago.
This question starts with the same words of a bunch of others... "I'm trying to do a card game in c#", i've seen a lot of similar questions, but no one is actually solving my problem.
I'll introduce my problem with a little scenario:
Player A plays card X with target B
Player B needs to choose a countercard Y or pass
Player B view contains his cards and each card is represented by a
picturebox.
To continue the game i need the controller of player B to await until he clicks on card
of type Y, or to pass the turn.
To me, the easiest way to do something like this is faking await using booleans. It's easy to record which was the last card played (E.g X) and then wait until the card.Click event is triggered on a card of type Y or if the pass button is pressed.
The problem with that is that it requires the use of a large number of boolean variables and if/else to handle those "events".
What would be a better implementation of something like this?
Edit.
GameController.cs
public void XCardPlayed(string target)
{
if (this.players[0].Username.Equals(target))
{
// I'm the target
lastCardPlayed = "X";
PlayYCard();
}
else
{
// display the card on the table
}
}
public void PlayYCard()
{
gameView.NotifyPlayYCard();
}
GameForm.cs
public void PlayCardFromHand(int index)
{
if (playY)
{
// Check if card at index is instance of Y
}
}
public void NotifyPlayYCard()
{
playY = true;
}
public void CardClick(object sender, EventArgs e)
{
PictureBox current = (PictureBox)sender;
PlayCardFromHand(pnl_cards.Controls.IndexOf(current));
}
In general these kinds of problems are solved by creating a statemachine, I.e. something that know what state the game is in, and depending on this state, how to handle the next input. There are several ways to program state machines, but one way is to use async/await to let the compiler do it.
You can wrap a button in a class that completes a task each time the button is pressed:
public class ButtonAwaiter
{
private readonly Button button;
private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
public ButtonAwaiter(Button button)
{
this.button = button;
this.button.Click += OnClick;
}
private void OnClick(object sender, EventArgs e)
{
tcs.SetResult(true);
tcs = new TaskCompletionSource<bool>();
}
public Task GetTask() => tcs.Task;
}
Substitute 'bool' for whatever the button represents.
And await when the first of the buttons is pressed like:
public async Task DoGameLoop()
{
var b1 = new ButtonAwaiter(button1);
var b2 = new ButtonAwaiter(button2);
while (GameIsInProgress)
{
var pressed = await Task.WhenAny(new[] {b1.GetTask(), b2.GetTask()});
}
}
This method is especially nice when the user needs to follow some sequence of actions since it lets you write the code in a reasonably straightforward manner.

What Unity API's are not allowed in async callbacks?

I saw somewhere that within a Thread in Unity, you couldn't use Unity API's.
I'm wondering if this is also the case for async callbacks in general (for example a function assigned to WebSocket.OnMessage when using WebSocketSharp), and if so then is there a way to know what is allowed and what isn't (i.e. what are "Unity API's")?
As an example, when using WebSocketSharp's WebSocket.OnMessage, I put this in my Start function of a MonoBehavior:
// ws is a WebSocketSharp.WebSocket
// displayText is a UnityEngine.UI.Text
ws.OnMessage += (sender, evt) =>
{
// 1
boo = UnityEngine.Random.Range(1, 1000).ToString();
// 2
displayText.text = "Heyoo";
};
The line under 1 errors (no logs just beyond it) but no error message shows. Whereas when that line is not inside this callback (top-level of Update for example), I can see its result no problem.
As for the line under 2, the Inspector in Unity shows the updated text, but the Play screen does not, until I update an attribute in the Inspector, as if the text field did get updated, but when it needed to use a Unity API to update the screen, it failed, so it's not until a separate update happens that it actually appears.
That's my hypothesis for these odd behaviors, so please let me know if that is correct, and if there's a succinct (or documented) way to describe what I'm describing.
async in general means exactly this: Not in the main thread.
It is hard to answer what is supported and what not in other threads then the mainthread ... short: Most things are not supported.
The UnityEngine.Random.Range(1, 1000).ToString(); should work. But be carefull with assignments!
A known workaround is to create like a callback worker and pass Actions to execute back to the main thread like e.g.:
public class MainThreadWorker : MonoBehaviour
{
// singleton pattern
public static MainThreadWorker Instance;
// added actions will be executed in the main thread
ConcurrentQueue<Action> actions = new ConcurrentQueue<Action>();
private void Awake()
{
if (Instance)
{
this.enabled = false;
return;
}
Instance = this;
}
private void Update()
{
// execute all actions added since the last frame
while (actions.TryDequeue(out var action))
{
action?.Invoke();
}
}
public void AddAction(Action action)
{
if(action != null) actions.Enqueue(action);
}
}
Having this in your scene somewhere you can now pass an action back to the main thread like
ws.OnMessage += (sender, evt) =>
{
MainThreadWorker.Instance.AddAction(()=>
{
// 1
boo = UnityEngine.Random.Range(1, 1000).ToString();
// 2
displayText.text = "Heyoo";
});
};

How to reduce frequency of continuously fired event's event handling

I am learning about tasks and async/await in c#. So please consider the stupidity of my question.
There is an event DummyEvent in a class. An event handler DummyEventHandler is subscribed to this event and it handles a large amount of CPU bound task, which is actually not needed to be used so frequently.
For that reason, if DummyEvent is fired continuously, I want DummyEventHandler to respond either at a reduced frequency, or respond at the end of that continuity.
So, my idea is to extract the large task into a separate Task and made it to delay 500 millisecond before it proceeds. After the delay ends, it will check whether the same Task has been scheduled again (continuous event fire) or not and avoid the large calculation if true.
Here is my naive implementation of that idea:
int ReducedCall = 0;
int TotalCallActual = 0;
protected void DummyEventHandler(object sender, bool arg)
{
TotalCallActual++;
LargeCPUBoundTask(); // there is a green underline here, but I think it's ok, or.. is it?
}
async Task LargeCPUBoundTask()
{
ReducedCall = TotalCallActual;
await Task.Delay(500);
// if this task is called again in this time, TotalCallActual will increase
if (ReducedCall == TotalCallActual)
{
// do all the large tasks
……
ReducedCall = 0;
TotalCallActual = 0;
}
}
But the problem is, I am not getting what I want. The line Task.Delay(500) doesn't actually await , or, if it does wait, there is something wrong because I experience staggering .
Any better idea, or any improvement / correction?
Ask for any additional information.
Thanks
You can leverage Reactive Extensions to do this:
void Main()
{
var generator = new EventGenerator();
var observable = Observable.FromEventPattern<EventHandler<bool>, bool>(
h => generator.MyEvent += h,
h => generator.MyEvent -= h);
observable
.Throttle(TimeSpan.FromSeconds(1))
.Subscribe(s =>
{
Console.WriteLine("doing something");
});
// simulate rapid firing event
for(int i = 0; i <= 100; i++)
generator.RaiseEvent();
// when no longer interested, dispose the subscription
subscription.Dispose();
}
public class EventGenerator
{
public event EventHandler<bool> MyEvent;
public void RaiseEvent()
{
if (MyEvent != null)
{
MyEvent(this, false);
}
}
}
The Throttle operator as coded above will allow a value (event) getting true every second.
So in the above code example the text doing something will only be printed once (after a second) even while the event is fired many times.
Edit
By the way, the reason for the green line is that your Task is not awaited. To fix it alter the code to:
protected async void DummyEventHandler(object sender, bool arg)
{
TotalCallActual++;
await LargeCPUBoundTask(); // there is no more green underline here
}
Unfortunately this will still not solve your issue as an event cannot be awaited so if the event is raised again while LargeCPUBoundTask is still running another call to LargeCPUBoundTask will be made so the work is overlapping if you get what I mean. In other words, that is why your code does not work.
I would use the timer event handler instead of your DummyEventHandler
Just adjust the frequency in milisencond of the timer and that will be it. You can create a timer via code without adding it to a form as a control. I think it is in the common controls lib.
Hope this helps. Good luck.
I spent some more time thinking about this problem and the assumption I made with my first solution was that the event is continuously firing, when it could just be firing part of the time for a while and then stop in the real problem.
In cases like this, the CPU bound task would only occur on the first event firing and then if the events finish firing before that CPU bound task completes, the remaining events would not get handled. But you wouldn't want to handle all of them, just the "last" one (not necessarily the actual last one, just one more to take care of the "cleanup").
So I've updated my answer to include the use case where there are frequent yet intermittent (i.e. burst of events then quiet) the correct thing would occur and a final run of the CPU bound task would happen (but still no more than 1 CPU bound task running at a time).
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Sender s = new Sender();
using (Listener l = new Listener(s))
{
s.BeginDemonstration();
}
}
}
class Sender
{
const int ATTEMPTED_CALLS = 1000000;
internal EventHandler frequencyReducedHandler;
internal int actualCalls = 0;
internal int ignoredCalls = 0;
Task[] tasks = new Task[ATTEMPTED_CALLS];
internal void BeginDemonstration()
{
int attemptedCalls;
for (attemptedCalls = 0; attemptedCalls < ATTEMPTED_CALLS; attemptedCalls++)
{
tasks[attemptedCalls] = Task.Run(() => frequencyReducedHandler.Invoke(this, EventArgs.Empty));
//frequencyReducedHandler?.BeginInvoke(this, EventArgs.Empty, null, null);
}
if (tasks[0] != null)
{
Task.WaitAll(tasks, Timeout.Infinite);
}
Console.WriteLine($"Attempted: {attemptedCalls}\tActual: {actualCalls}\tIgnored: {ignoredCalls}");
Console.ReadKey();
}
}
class Listener : IDisposable
{
enum State
{
Waiting,
Running,
Queued
}
private readonly AutoResetEvent m_SingleEntry = new AutoResetEvent(true);
private readonly Sender m_Sender;
private int m_CurrentState = (int)State.Waiting;
internal Listener(Sender sender)
{
m_Sender = sender;
m_Sender.frequencyReducedHandler += Handler;
}
private async void Handler(object sender, EventArgs args)
{
int state = Interlocked.Increment(ref m_CurrentState);
try
{
if (state <= (int)State.Queued) // Previous state was WAITING or RUNNING
{
// Ensure only one run at a time
m_SingleEntry.WaitOne();
try
{
// Only one thread at a time here so
// no need for Interlocked.Increment
m_Sender.actualCalls++;
// Execute CPU intensive task
await Task.Delay(500);
}
finally
{
// Allow a waiting thread to proceed
m_SingleEntry.Set();
}
}
else
{
Interlocked.Increment(ref m_Sender.ignoredCalls);
}
}
finally
{
Interlocked.Decrement(ref m_CurrentState);
}
}
public void Dispose()
{
m_SingleEntry?.Dispose();
}
}

For-loop multi-threading passes upper bound

We are learning multi-threadding today in class and we came across a very curious error. When doing a for loop in our new thread the upper bound of the for loop keeps getting passed. The thread is being killed but then another value will appear and end another thread.
For the purpose of debugging the error I changed the upper bound to 90 to avoid the OutOfRange Exception on the progressbar.
While outputting the counter to the progressing bar and updating the progress bar I got this in my output window.
If i commented out the updating on the progress bar (pbLoad.Value = i;) I got this in my output window
I have tried changing the loop to i<101 and also tried moving where the i++ was but it made no difference
EDIT: This is coming from the BeginInvoke. When i switched it to Invoke it worked but then I will get a deadlock when trying to use the cancel button.
Here is the code:
public partial class Form1 : Form
{
Thread backgroundThread;
bool stopExecution = false;
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
stopExecution = false;
btnStart.Enabled = false;
backgroundThread = new Thread(DoDomethingThatTakesAWhile);
backgroundThread.Start();
}
private void DoDomethingThatTakesAWhile()
{
for (int i = 0; i <= 100; i++)
{
if (!stopExecution)
{
Thread.Sleep(100);
if (pbLoad.InvokeRequired)
{
MethodInvoker myMethod
= new MethodInvoker(
delegate
{
if (!stopExecution)
{
pbLoad.Value = i;
Debug.WriteLine(i); //i to output window
}
});
pbLoad.BeginInvoke(myMethod);
}
else
{
pbLoad.Value = i;
}
}
else
{
break;
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
//backgroundThread.Abort();
stopExecution = true;
backgroundThread.Join();
pbLoad.Value = 0;
btnStart.Enabled = true;
}
}
When you call MethodInvoke it will not occurs at that moment, but some time later.
In your scenario you have a chance of following to occurs:
invoked code is finally executed;
the loop is already finished (and i become 101)
you are accessing i directly and you read 101.
And to fix it you can make a copy of i (by passing it as a parameter to invoked method):
pbLoad.BeginInvoke(new Action<int>(a =>
{
if (!stopExecution)
{
pbLoad.Value = a;
Debug.WriteLine(a); //a to output window
}
}), new object[] { i });
P.S: you don't need to check for InvokeRequired, unless you plan to call DoDomethingThatTakesAWhile method directly, which I assume is not the case.
You're using BeginInvoke which explicitly opens the possibility for races. I recommend synchronous invoking.
Furthermore, you are capturing i, not its value. This is racy and only works by accident because you're sleeping.
Either of the changes will fix the problem. Do both of them.
If you can, abolish this low-level use of synchronization and use async/await.

C# BackgroundWorker

I have a button that on click event I get some information from the network.
When I get information I parse it and add items to ListBox. All is fine, but when I do a fast double-click on button, it seems that two background workers are running and after finishing all work, items in the list are dublicated.
I want to do so that if you click button and the proccess of getting information is in work, this thread is stopping and only after first work is completed the second one is beginning.
Yes, I know about AutoResetEvent, but when I used it it helped me only one time and never more. I can't implement this situation and hope that you will help me!
Now I even try to make easier but no success :( : I added a flag field(RefreshDialogs)(default false), when the user clicks on button, if flag is true(it means that work is doing), nothing is doing, but when flag field is set to false, all is fine and we start a new proccess.
When Backgroundwork completes, I change field flag to false(it means that user can run a new proccess).
private void Message_Refresh_Click(object sender, EventArgs e)
{
if (!RefreshDialogs)
{
RefreshDialogs = true;
if (threadBackgroundDialogs.WorkerSupportsCancellation)
{
threadBackgroundDialogs.CancelAsync();
}
if (!threadBackgroundDialogs.IsBusy)
{
downloadedDialogs = 0;
threadBackgroundDialogs = new BackgroundWorker();
threadBackgroundDialogs.WorkerSupportsCancellation = true;
threadBackgroundDialogs.DoWork += LoadDialogs;
threadBackgroundDialogs.RunWorkerCompleted += ProcessCompleted;
threadBackgroundDialogs.RunWorkerAsync();
}
}
}
void ProcessCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RefreshDialogs = false;
}
So you want to keep the second process running while the first works, but they shouldn't disturb each other? And after the first one finishes the second one continues?
Crude way: While loop:
if (!RefreshDialogs)
{
RefreshDialogs = true;
this becomes:
while(RefreshDialogs)
{
}
RefreshDialogs = true;
After you set it false the second process wwill jump out of the while. (Note this is extremly inefficent since both processes will be running all the time, i'm pretty sure the second one will block the first one, but with multitasking now it shouldn't, if it block use a Dispatcher.Thread)
Elegant way: Use A Semaphore
http://msdn.microsoft.com/de-de/library/system.threading.semaphore%28v=vs.80%29.aspx
If you find it impossible to have both processes running at the same time, or want another way:
Add an Array/List/int and when the second process notices there is the first process running, like with your bool, increase your Added variable, and at the end of the process, restart the new process and decrese the variable:
int number;
if (!RefreshDialogs)
{
RefreshDialogs = true;
your code;
if(number > 0)
{
number--;
restart process
}
}
else
{
number++;
}
I have to admit, i like my last proposal the most, since its highly efficent.
Make your thread blocking. That is easy;
lock(someSharedGlobalObject)
{
Do Work, Exit early if cancelled
}
This way other threads will wait until the first thread releases the lock. They will never execute simultaneously and silently wait until they can continue.
As for other options; why not disable the button when clicked and re-enable it when the backgroundworker completes. Only problem is this does not allow for cancelling the current thread. The user has to wait for it to finish. It does make any concurrency go away very easily.
How about this approach?
Create a request queue or counter which will be incremented on every button click. Every time that count is > 0. Start the background worker. When the information comes, decrement the count and check for 0. If its still > 0 restart the worker. In that your request handler becomes sequential.
In this approach you may face the problem of continuous reference of the count by two threads, for that you may use a lock unlock condition.
I hav followed this approach for my app and it works well, hope it does the same for you.
I'm not an Windows Phone expert, but as I see it has support for TPL, so following code would read nicely:
private object syncRoot =new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
latestTask = Task.Factory.StartNew(action);
else
latestTask = latestTask.ContinueWith(tsk => action());
}
}
Use can use semaphores
class TheClass
{
static SemaphoreSlim _sem = new SemaphoreSlim (3);
static void Main()
{
for (int i = 1; i <= 5; i++)
new Thread (Enter).Start (i);
}
static void Enter (object name)
{
Console.WriteLine (name + " wants to enter");
_sem.Wait();
Console.WriteLine (name + " has entered!");
Thread.Sleep (1000 * (int) name );
Console.WriteLine (name + " is leaving");
_sem.Release(); }
}
}
I found the solution and thanks to #Giedrius. Flag RefreshingDialogs is set to true only when proccess is at the end, when I added items to Listbox. The reason why I'am using this flag is that state of process changes to complete when the asynchronous operation of getting content from network(HttpWebRequest, method BeginGetRequestStream) begins, but after network operaion is complete I need to make UI operations and not only them(parse content and add it to Listbox)My solution is:
private object syncRoot = new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
{
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
else if(latestTask.IsCompleted && !RefreshingDialogs)
{
RefreshingDialogs = true;
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
}
}
private void Message_Refresh_Click(object sender, EventArgs e)
{
Action ac = new Action(LoadDialogs2);
EnqueueAction(ac);
}

Categories