For my producer-consumer application, I clicked the button to trigger it. However it freezes. The basic idea of the project is to find how many even numbers in the queue when clicking Start button. Click Cancel button to stop it. I want to print the result on the console. But both are not working, which means no output(empty on the screen). It may be deadlock? Anyway the screen freezes.
The entire clean code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace CancellationTokenStop
{
public partial class Form1 : Form
{
public static BufferBlock<int> m_Queue = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1000 });
private static int evenNumber;
private static CancellationTokenSource cTokenSource;
private static CancellationToken cToken;
private static Object obj = new object();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
public Form1()
{
InitializeComponent();
AllocConsole();
}
private void btnCancel_Click(object sender, EventArgs e)
{
cTokenSource.Cancel();
}
private void btnStart_Click(object sender, EventArgs e)
{
try
{
cTokenSource = new CancellationTokenSource();
cToken = cTokenSource.Token;
var producer = Producer();
var consumer = Consumer();
Task.WhenAll(producer, consumer).Wait();
Report();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException);
Console.Read();
}
}
static async Task Producer()
{
for (int i = 0; i < 200; i++)
{
// Send a value to the consumer and wait for the value to be processed
await m_Queue.SendAsync(i);
}
// Signal the consumer that there will be no more values
m_Queue.Complete();
}
static async Task Consumer()
{
try
{
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 4
};
var consumerBlock = new ActionBlock<int>(x =>
{
DoWork(x, cToken);
if (x % 2 == 0)
// Increment the counter in a thread-safe way
Interlocked.Increment(ref evenNumber);
}, executionDataflowBlockOptions);
// Link the buffer to the consumer
using (m_Queue.LinkTo(consumerBlock, new DataflowLinkOptions { PropagateCompletion = true }))
{
// Wait for the consumer to finish.
// This method will exit after all the data from the buffer was processed.
await consumerBlock.Completion;
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
Console.Read();
}
}
static void DoWork(int x, CancellationToken cToken)
{
cToken.Register(() =>
{
Console.WriteLine("Stop at "+x);
});
Thread.Sleep(100);
}
public static void Report()
{
Console.WriteLine("There are {0} even numbers", evenNumber);
}
}
}
The producer part is simple, it just sends data to a BufferBlock. Consumer part is complicated, using ActionBlock and pass a cancellation token.
The expected result is stored in the variable evenNumber.
You are blocking your UI in your button click with the .Wait you need to await instead.
private async void btnStart_Click(object sender, EventArgs e)
{
try
{
cTokenSource = new CancellationTokenSource();
cToken = cTokenSource.Token;
var producer = Producer();
var consumer = Consumer();
await Task.WhenAll(producer, consumer);
Report();
}
catch (AggregateException ex)
{
Console.WriteLine(ex.InnerException);
Console.Read();
}
}
Related
I have the following c# functions that allow me to receive messages from a given queue from Azure, it works fine on console apps :
public async Task MainReceiver()
{
queueClient = new QueueClient(connectionString, queueName);
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 5,
AutoComplete = false,
};
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
Console.ReadLine();
await queueClient.CloseAsync();
}
public async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
var jsonString = Encoding.UTF8.GetString(message.Body);
Console.WriteLine($"Person Received: { jsonString }");
List<string> data = new List<string>();
if (messageLists.ContainsKey(queueName))
{
data = messageLists[queueName];
messageLists.Remove(queueName);
data.Add(jsonString);
}
else
{
data.Add(jsonString);
}
messageLists.Add(queueName, data);
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
My goal now is to list the messages using a listbox control.
But if i try to call the same main methode in a button click, nothing happends, in debug mode i noticed that my breakpoint in ProcessMessagesAsync is never reached by my win form app.
public async void button2_Click(object sender, EventArgs e)
{
await receiveMessage();
}
public async Task receiveMessage()
{
await messages.MainReceiver();
List<string> data = new List<string>();
data = messages.messageLists["eventsqueue"];
for (int i = 0; i < messages.messageLists.Count; i++)
listBox1.Items.Add(data[i]);
}
I tried to use to queue my task in the thread pool using the following code , but it didn't help :
ThreadPool.QueueUserWorkItem(async (w) => await messages.MainReceiver());
Please follow the below workaround to achieve Service Bus queue messages into Windows Forms
Three major steps to show your data into LISTBOX
Create a list box using the ListBox() constructor is provided by the ListBox class.
ListBox listBox1 = new ListBox();
Set/Add the Items property of the ListBox provided by the ListBox class.
listBox1.Items.Add("your data to add in listbox");
Add this ListBox control to the form using Add() method.
this.Controls.Add(listBox1);
Here I am following the above steps.
Program.cs
using System.Collections.Generic;
using System.Windows.Forms;
namespace servicebuswinform
{
using Microsoft.Azure.ServiceBus;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
const string ServiceBusConnectionString = <ServiceBus Connection string>;
const string QueueName = "<Queue name>";
static IQueueClient queueClient;
public static List<string> data = new List<string>();
[STAThread]
public static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static async Task MainAsync()
{
const int numberOfMessages = 10;
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
RegisterOnMessageHandlerAndReceiveMessages();
// Send Messages
await SendMessagesAsync(numberOfMessages);
await queueClient.CloseAsync();
}
public static void RegisterOnMessageHandlerAndReceiveMessages()
{
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
// Register the function that will process messages
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
public static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
//Adding message into list
data.Add("Received message: SequenceNumber:"+ message.SystemProperties.SequenceNumber.ToString()+" Body:" + Encoding.UTF8.GetString(message.Body));
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
// Use this Handler to look at the exceptions received on the MessagePump
public static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
public static async Task SendMessagesAsync(int numberOfMessagesToSend)
{
try
{
for (var i = 0; i < numberOfMessagesToSend; i++)
{
// Create a new message to send to the queue
string messageBody = $"Message {i}";
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
// Write the body of the message to the console
Console.WriteLine($"Sending message: {messageBody}");
// Send the message to the queue
await queueClient.SendAsync(message);
}
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}");
}
}
}
}
Form.cs
Here I am adding one button if we hit the button it will get the service bus queue messages and it will be added in Listbox.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace servicebuswinform
{
public partial class Form1 : Form
{
public static List<string> data1 = new List<string>();
public Form1()
{
InitializeComponent();
}
public async void button1_Click(object sender, EventArgs e)
{
await receiveMessage();
ListBox listBox1 = new ListBox();
this.listBox1.Visible = true;
for (int i = 0; i < data1.Count; i++)
{
listBox1.FormattingEnabled = true;
listBox1.Enabled = true;
listBox1.Size = new System.Drawing.Size(692, 214);
listBox1.Location = new System.Drawing.Point(1, 131);
listBox1.MultiColumn = true;
listBox1.SelectionMode = SelectionMode.MultiExtended;
listBox1.BeginUpdate();
listBox1.Items.Add("item" + data1[i].ToString());
}
this.Controls.Add(listBox1);
}
public static async Task receiveMessage()
{
await Program.MainAsync();
data1 = Program.data;
}
private void Form1_Load(object sender, EventArgs e)
{
this.listBox1.Visible = false;
}
}
}
You can follow the above steps which I have mentioned. Using this I can see the list of service bus queue messages in windows forms
I'm trying to create a file copy application. I have a BackgroudWorker doing the work and it works fine so far. How it works, I have a form, Source and Destination Folder fields and a Copy button. The Copy button triggers the Copy operation using the BackgroundWorker. The Progressbar gets updated etc. Now I need to implemet a queue type of operation. I need to add other Source and Destination Copy operation and add it to the queue. I tried using the following:
Queue<MethodInvoker> MyQueue = new Queue<MethodInvoker>();
MyQueue.Enqueue(new MethodInvoker(() =>CopyStuff(1)));
MyQueue.Enqueue(new MethodInvoker(() =>CopyStuff(2)));
MethodInvoker bb = MyQueue.Dequeue(); //(I Forgot this part)
bb();
bb = MyQueue.Dequeue();
bb();
The problem is, because it's a BackgroundWorker, it does not wait for the first operation to complete. Any suggestions on how to go about this?
After fixing my code it works, except for it running on the UI thread, locking controls.
Update 1:
This works, but runs on main thread, user can't use controls while it's running:
BlockingCollection<MethodInvoker> bCollection = new BlockingCollection<MethodInvoker>(boundedCapacity: 2);
Task producerThread = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 2; ++i)
{
bCollection.Add(CopyStuff);
}
bCollection.CompleteAdding();
});
foreach (MethodInvoker item in bCollection.GetConsumingEnumerable())
{
BeginInvoke(item);
}
Update 2:
Works in a Console App, but not a Windows Forms Application.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsyncCopy
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public class AsyncCopy
{
private static Queue<Action> MyQueue = new Queue<Action>();
public async Task EnqueueAndCopy(Action[] actionList)
{
foreach (var action in actionList)
{
MyQueue.Enqueue(action);
}
while (MyQueue.TryDequeue(out var copyAction)) //Here's the problem on Windows Form Applivcation
{
//If the copyaction is itself async, await directly on the method here instead of running the action in a Task
await Task.Factory.StartNew(copyAction);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
var asyncCopy = new AsyncCopy();
//In a typical usage, this will call be awaited
//In your case, make this call from a async method and call it from the UI thread
asyncCopy.EnqueueAndCopy(
new Action[] {
() => CopyStuff (1),
() => CopyStuff (2)
}
);
//Loop to confirm the processing is asynchronous
for (int i = 0; i <= 20; i++)
{
Console.WriteLine($"{i}");
Thread.Sleep(300);
}
}
//Copy process simulation
static void CopyStuff(int i)
{
Console.WriteLine($"Copying {i}");
Thread.Sleep(3000);
Console.WriteLine($"Copied {i}");
}
}
}
If you need to Queue the Copy process asynchronously as the other copies are processing, I would recommend a producer consumer pattern. Refer https://www.dotnetcurry.com/patterns-practices/1407/producer-consumer-pattern-dotnet-csharp
But a simple async await would work in your case as well
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace stackoverflow {
class Program {
static void Main (string[] args) {
var asyncCopy = new AsyncCopy ();
//In a typical usage, this will call be awaited
//In your case, make this call from a async method and call it from the UI thread
asyncCopy.EnqueueAndCopy (
new Action[] {
() => CopyStuff (1),
() => CopyStuff (2)
}
);
//Loop to confirm the processing is asynchronous
for(int i=0; i <= 20; i++)
{
Console.WriteLine($"{i}");
Thread.Sleep(300);
}
}
//Copy process simulation
static void CopyStuff (int i) {
Console.WriteLine ($"Copying {i}");
Thread.Sleep(3000);
Console.WriteLine ($"Copied {i}");
}
}
public class AsyncCopy {
private static Queue<Action> MyQueue = new Queue<Action> ();
public async Task EnqueueAndCopy (Action[] actionList) {
foreach (var action in actionList) {
MyQueue.Enqueue (action);
}
while (MyQueue.TryDequeue (out var copyAction)) {
//If the copyaction is itself async, await directly on the method here instead of running the action in a Task
await Task.Factory.StartNew (copyAction);
}
}
}
}
Update
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async Task CopyAsync(IEnumerable<Action> actionList)
{
foreach (var action in actionList)
{
await Task.Factory.StartNew(action);
}
}
private async void button2_Click(object sender, EventArgs e)
{
await CopyAsync(
new Action[]
{
() => CopyStuff(1),
() => CopyStuff(2)
});
}
//Copy process simulation
static void CopyStuff(int i)
{
Thread.Sleep(3000);
MessageBox.Show(string.Format("File Copied {0}", i));
}
}
}
I have a function which it is a resources (CPU and time) consuming, I need it to run always (while true) but I want an option to wait\ halt (but only that the function will wait and not the thread).
I have tried to create a task that runs a thread soi will be able to wait for the task to finish while being set and reset on the thread.
the code is below.
it doesn't work since the task ends without waiting for the thread to be set\reset.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private static ConcurrentDictionary<int, Task> waitingTasks = new ConcurrentDictionary<int, Task>();
private static ConcurrentDictionary<int, ManualResetEvent> manualResetEvents = new ConcurrentDictionary<int, ManualResetEvent>();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
System.Timers.Timer timer;
timer = new System.Timers.Timer(60000);
timer.Elapsed += Timer_Elapsed;
whileTrue(1);
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (false)// some condition
{
releasePerPrefix(1);
}
}
private void whileTrue(int index)
{
while (true)
{
if (true)// some condition
{
Task waiting = getWaitingTask(index);
Console.WriteLine("start waiting " + DateTime.Now);
waiting.Wait();
///HERE IS THE PROBLEAM - the waiting task continue imedialty without it being halt\wait\pause
Console.WriteLine("Released "+DateTime.Now);
}
doJob();
}
}
private void doJob()
{
Console.WriteLine("doJob " + DateTime.Now);
}
private Task getWaitingTask(int prefix)
{
if (!waitingTasks.TryGetValue(prefix, out Task task) || task.IsCompleted)
{
task = Task.Factory.StartNew(() =>
{
Thread thread = new Thread(new ThreadStart(() => waitPerPrefix(prefix)));
thread.Start();
thread.Join();
});
waitingTasks.TryAdd(prefix, task);
}
return task;
}
private static void waitPerPrefix(int prefix)
{
if (!manualResetEvents.TryGetValue(prefix, out ManualResetEvent manualResetEvent))
{
manualResetEvent = new ManualResetEvent(false);
manualResetEvents.TryAdd(prefix, manualResetEvent);
}
manualResetEvent.Reset();
}
private static void releasePerPrefix(int prefix)
{
if (!manualResetEvents.TryGetValue(prefix, out ManualResetEvent manualResetEvent))
{
manualResetEvent = new ManualResetEvent(true);
manualResetEvents.TryAdd(prefix, manualResetEvent);
}
manualResetEvent.Set();
}
}
}
I have a Winform application that needs to manage several state machines concurrently. Each state machine may have a socket, serial port or other external connection.
Here is my state machine shell implemented using async/await and dataflow BlockBuffer as a message queue.
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
public class StateMachineMessage
{
private static int id = 1;
public int Id { get; } = 0;
public string Name { get; }
public string Text { get; }
public BufferBlock<StateMachineMessage> Queue { get; set; }
public StateMachineMessage(string name, string text)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = null;
}
public StateMachineMessage(string name, string text, BufferBlock<StateMachineMessage> queue)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = queue;
}
public override string ToString()
{
return "(" + Id + ":" + Name + ":" + Text + ")";
}
}
public class StateMachine
{
private int State { get; }
public string Name { get; }
RichTextBox TextBox { get; }
BufferBlock<StateMachineMessage> Queue { get; }
public StateMachine(string name, BufferBlock<StateMachineMessage> queue, RichTextBox textBox)
{
Name = name;
Queue = queue;
TextBox = textBox;
}
public async Task StartAsync()
{
while (await Queue.OutputAvailableAsync())
{
var request = await Queue.ReceiveAsync();
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, request));
if (request.Queue != null)
{
var response = new StateMachineMessage("RESPONSE", "Good Morning!");
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, response));
await request.Queue.SendAsync(response);
}
}
}
}
The main form creates the dataflow block buffers and state machines on initialization.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace DataflowThread
{
public partial class Form1 : Form
{
// Enables the user interface to signal cancellation.
CancellationTokenSource cancellationSource;
// The worker state machine and dataflow node
StateMachine workerMachine;
BufferBlock<StateMachineMessage> workQueue;
// The main thread state machine
StateMachine mainMachine;
BufferBlock<StateMachineMessage> mainQueue;
// Enables progress bar actions to run on the UI thread.
TaskScheduler uiTaskScheduler;
public Form1()
{
InitializeComponent();
InitializeDataflow();
StartStateMachines();
}
public void InitializeDataflow()
{
// Create the cancellation source.
cancellationSource = new CancellationTokenSource();
// Create the UI task scheduler from the current sychronization context.
uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// Create dataflow options
var options = new ExecutionDataflowBlockOptions();
options.CancellationToken = cancellationSource.Token;
options.TaskScheduler = uiTaskScheduler;
// Create dataflow nodes for the main thread and worker state machines
mainQueue = new BufferBlock<StateMachineMessage>(options);
workQueue = new BufferBlock<StateMachineMessage>(options);
// Create the state machines for the worker and main thread
mainMachine = new StateMachine("MainMach", mainQueue, richTextBox1);
workerMachine = new StateMachine("WorkerMach", workQueue, richTextBox2);
}
public void StartStateMachines()
{
var mainTask = mainMachine.StartAsync();
var workerTask = workerMachine.StartAsync();
}
~Form1()
{
cancellationSource.Dispose();
}
private void sendButton_Click(object sender, EventArgs e)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
private async void cancelButton_Click(object sender, EventArgs e)
{
try
{
workQueue.Complete();
mainQueue.Complete();
await Task.WhenAll(workQueue.Completion, mainQueue.Completion);
richTextBox1.AppendText("All queues Completed\n");
resetButton.Enabled = true;
}
catch (OperationCanceledException ex)
{
richTextBox1.AppendText(ex.ToString());
}
}
private void sendTenButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i ++)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
}
private void resetButton_Click(object sender, EventArgs e)
{
resetButton.Enabled = false;
InitializeDataflow();
StartStateMachines();
richTextBox1.Clear();
richTextBox2.Clear();
}
}
}
One started each state machine loops on the BlockBuffer until cancelled. My question:
What is the correct async/await usage for the outermost StartStateMachines() method?
Silently ignoring the Tasks returned from each state machine start feels funny. I am relatively new to C# and am trying to use the async / await patterns where I would normally use a message queue and thread. I am also trying to be a bit modern and not use Invoke/BeginInvoke everywhere. Any help is appreciated.
Thanks in advance.
Sean
EDIT - I added the full state machine source and Program.cs here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DataflowThread
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
I am working on xamarin forms PCL + iOS. I want to cancel a task when it enters background. And start it all over when app enters foreground.
This is what I have tried so far.. I am not sure if my way cancels any task or what is it that is happening here?
async void getData()
{
bool isSuccess = await getSomeData();
if(isSuccess)
await getSomeMoreData();
}
CancellationTokenSource cts;
async Task<bool> getSomeData()
{
cts = new CancellationTokenSource();
AppEntersBackgorund += (sender,args) => { cts. cancel();});
CancellationToken token = new CancellationToken();
token = cts.token;
await Task.Run(() => {
token.ThrowIfCancellationRequested();
isSuccess = ParserData(token); // parsedata also checks periodically if task is cancelled
},token); //what happens here when cancel called?
return isSuccess;
}
async void getSomeMoreData()
{
if(!cts.IsCancellationRequested)
cts = new CancellationTokenSource();
AppEntersBackgorund += (sender,args) => { cts. cancel();});
CancellationToken token = new CancellationToken();
token = cts.token;
await Task.Run(() =>
{
token.ThrowIfCancellationRequested();
ParseSomeMoreData(token);
},token);
}
When app enters foregorund, I again call the getData() method so that i start all over again.
What happens is that, Task is not getting cancelled, rather getSomeMoreData is getting called twice ( or the no. of times the app goes from background to foreground) .
Can someone explain how I can achieve this? And what is happening here?
Actually, this is not a Xamarin problem, it is just a C# problem except the app's enter foreground/background events.
For the requirements you need, you should make a task manager object to implement it.
I wrote a sample code for you:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace BackGroundTask
{
public class TaskManager
{
//The instance property
private static TaskManager instance;
public static TaskManager Instance{
get{
if(null == instance)
instance = new TaskManager();
return instance;
}
}
private bool actionTaskFreeFlag = true;//Flag for if actionTask is available or not
private Queue<Action> taskQueue;//A queue to collect the tasks you added into the manager
private Task scanTask;//A Task to sacn the queue
private Task actionTask;//A task to do the current action
private Thread actionTaskRunningThread;//Record the thread that current action is working on
public TaskManager()
{
taskQueue = new Queue<Action>();
scanTask = new Task(() =>
{
while (true)
{
if (actionTaskFreeFlag && taskQueue.Count > 0)//If there still something to do and the actionTask is available then do the action
{
actionTaskFreeFlag = false;
Action action = taskQueue.Dequeue();
actionTask = new Task(() => {
actionTaskRunningThread = System.Threading.Thread.CurrentThread;
action();
});
actionTask.Start();
actionTask.ContinueWith(delegate {
actionTaskFreeFlag = true;
});
}
}
});
scanTask.Start();
}
public void AddAction(Action action)
{
taskQueue.Enqueue(action);
}
public void CancelCurrentTaskAndClearTaskQueue()
{
Console.WriteLine("CancelCurrentTaskAndClearTaskQueue");
if(null != actionTaskRunningThread)
actionTaskRunningThread.Abort();
taskQueue.Clear();
}
}
}
And this is a sample code for how to use it to do the stuff you want:
//App enter background event
AppDelegate.Instance.AppDidEnterBackground += delegate {
TaskManager.Instance.CancelCurrentTaskAndClearTaskQueue();
};
//App enter forcenground event
AppDelegate.Instance.AppWillEnterForeground += delegate {
if (AppDelegate.FlagForGetData)
{
TaskManager.Instance.AddAction(GetData);
TaskManager.Instance.AddAction(GetMoreData);
}
};
And this is the methods for testing:
private void GetData()
{
AppDelegate.FlagForGetData = true;
Console.WriteLine("Began getting data.");
System.Threading.Thread.Sleep(5000);
AppDelegate.FlagForGetData = false;
Console.WriteLine("Getting data succeed.");
}
private void GetMoreData()
{
Console.WriteLine("Began getting more data.");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Getting more data succeed.");
}
Hope it can help you.