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();
}
}
}
Related
This question already has answers here:
Why is the console window closing immediately once displayed my output?
(15 answers)
Closed 1 year ago.
I wanted to test if I can trigger an event using method working in a new task.
when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
The output is nothing!
However, when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
//Here is the change
method1();
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
the output is this:
The threshold was reached
The threshold was reached
this is strange!
I could not understand why it prints twice.
However, I excepted it will not work with method1 is running in new task, because it will not be working in the same thread
could someone explain why?
and is there a way to communicate a concurrent method with the parent thread?
thanks in advance
As mentioned in comment, your code finishes before the task will run and pring anything. You should modify your code in async manner to await the task to finish
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
subscriber f = new subscriber();
await f.Test();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
}
public async Task Test()
{
await x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public async Task method2()
{
await Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
As for your second snippet, that looks like the Task has time to run and pring while the main thread printing inside method1. Some times it will pring twice (if have enaught of time) or will print once (if Console.Write in main thread will finish before the task will run).
Additional note: please be aware that your event handler will be called not in main thread, but in the thread in which task is executed (typically, this will be a thread from a thread pool)
When you call Task.Run(...), the action inside Run() will be started in new thread.
In your first case you start the new thread with Task.Run(), but the program finishes and exits before it executes that the new thread.
You can prevent the main thread from terminating by adding Console.Read() at the end of Main(). This way you can wait for the second thread to finish.
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
Console.Read();
}
}
In your second case you will get two messages because of this:
public void method2()
{
method1();
Task.Run(() => method1());
}
This time since you are directly calling method1() you will for sure write the message once in the console. Since printing on the console (any IO in general) is relatively slow, the second thread (created by Task.Run()) will have time to execute, and print the message second time.
Another solution is to create the thread manually
public void method2()
{
Thread t1 = new Thread(() => method1());
t1.IsBackground = false;
t1.Start();
}
The program will not exit until all foreground threads have finished. So when you set IsBackground = false;, the program will wait for that thread
I would like to add more delay in delay var while the execution waits
Example:
private System.Threading.Tasks.Task delayVar; //Delay var
private async void createDelay() //First function
{
delayVar = System.Threading.Tasks.Task.Delay(milliseconds);
await delayVar;
}
private void addDelay() //Second function
{
delayVar.Milliseconds +=5000;
}
Thanks.
You can't "reset" a Task.Delay, but you can reset a timer which makes it an ideal candidate to solve this problem.
Here's an example:
private System.Threading.Timer timer;
public void Start()
{
timer = new System.Threading.Timer(_ => fireMyCode());
restartTimer();
}
private void onFileChanged(object sender, EventArgs e)
{
restartTimer();
}
private void restartTimer()
{
timer.Change(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
But you don't have to use timers, you can still use Task.Delay with an additional task: the idea is to wait on two tasks, the delay and waiting for the files to change (you can use TaskCompletionSource to "create" a task from an event). If the delay task completes first, fire your code.
Here's an example:
TaskCompletionSource<object> fileChanged = new TaskCompletionSource<object>();
private void onFileChanged(object sender, EventArgs e)
{
fileChanged.TrySetResult(null);
}
private async Task endlessLoop()
{
while (true)
{
await handleFilesNotChanged();
}
}
private async Task handleFilesNotChanged()
{
Task timeout = Task.Delay(TimeSpan.FromMinutes(5));
Task waitForFile = fileChanged.Task;
if (await Task.WhenAny(timeout, waitForFile) == timeout)
{
fireMyCode();
}
fileChanged = new TaskCompletionSource<object>();
}
I have a message bus class which use Rx to push multiple threads events in WPF application.
My problem ObserveOnDispatcher does not invoke the event handler in the UI thread.
Code:
private void button_Click(object sender, RoutedEventArgs e)
{
var messageBus = new MessageBus();
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
Trace.WriteLine("Main Thread Id:" + Thread.CurrentThread.ManagedThreadId);
var deviceManager = new DeviceManager(messageBus);
deviceManager.Start();
}
private void TestHanlder(Message message)
{
Trace.WriteLine("UI Handler ThreadId:" + Thread.CurrentThread.ManagedThreadId);
}
public class DeviceManager
{
private readonly MessageBus _messageBus;
public DeviceManager(MessageBus messageBus)
{
_messageBus = messageBus;
}
public void Start()
{
for (;;)
{
var t = Task.Factory.StartNew(() => BackGroundTask(), TaskCreationOptions.LongRunning);
t.Wait();
}
}
private void BackGroundTask()
{
Thread.Sleep(1000);
Trace.WriteLine("Push ThreadId:" + Thread.CurrentThread.ManagedThreadId);
var message = new Message();
_messageBus.Publish(message);
}
}
public class MessageBus
{
readonly ISubject<object> _messages;
public MessageBus()
{
_messages = new Subject<object>();
}
public void Publish<TMessage>(TMessage message)
{
_messages.OnNext(message);
}
public IObservable<TMessage> GetMessages<TMessage>()
{
return _messages.OfType<TMessage>();
}
}
public class Message
{
public Message()
{
}
}
Without ObserveOnDispatcher:
messageBus.GetMessages<Message>().Subscribe(x => TestHanlder(x));
..........................Output.................................
Main Thread Id:8
Push ThreadId:9
UI Handler ThreadId:9
But I need to execute the TestHanlder function in the main thread or UI thread, in my use case above it must be the thread number 8.
When I use ObserveOnDispatcher:
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
..........................Output.................................
Main Thread Id:9
Push ThreadId:10
------------------> UI Handler ThreadId: are missing not there!?
What I'm doing wrong here?!!!!!
for (;;) { t.Wait(); } this code is executed in the UI thread and prevents it from executing anything else dispatched on it. ObserveOnDispatcher is working fine, but your dispatcher thread is blocked.
If you introduce async/await (that will release the thread), the scenario will work fine:
private async void button_Click(object sender, RoutedEventArgs e)
{
var messageBus = new MessageBus();
messageBus.GetMessages<Message>().ObserveOnDispatcher().Subscribe(x => TestHanlder(x));
Trace.WriteLine("Main Thread Id:" + Thread.CurrentThread.ManagedThreadId);
var deviceManager = new DeviceManager(messageBus);
await deviceManager.Start();
}
...
public async Task<Unit> Start()
{
for (;;)
{
await Task.Factory.StartNew(() => BackGroundTask(), TaskCreationOptions.LongRunning);
}
}
I want my program to wait after below line
frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
as above method is internally calling thread through StartProcessWithProgress() method . I want that thread to be completed before //code logic -2 line gets executed. At the same time, It should not stop UI update done by frmProgressBar.UpdateProgress(). How do I do this?
namespace NS1
{
public partial class frmMain : Form
{
private void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
//code logic - 2
MessageBox.Show("This is executing immediately.
I want to wait until above thread is complete");
}
}
public partial class frmProgressBar : Form
{
public void UpdateProgress(String strTextToDisplayOnProgress)
{
progressBar1.BeginInvoke(
new Action(() =>
{
progressBar1.Value++;
lblFileName.Text = strTextToDisplayOnProgress;
if (progressBar1.Value == progressBar1.Maximum)
{
this.Hide();
}
}));
}
public delegate void DelProgress();
public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount)
{
InitializeProgress(maxCount);
Thread backgroundThread = new Thread(new ThreadStart(delMethodCode));
backgroundThread.Start();
}
}
public static class PullMSI
{
public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI)
{
frmProgressBar frmProgressBar = new frmProgressBar();
frmProgressBar.StartProcessWithProgress(() =>
{
//StreamRader sr declaration and other code
while (!sr.EndOfStream)
{
//logic here
frmProgressBar.UpdateProgress("Copying sr.msiname");
}
}, 2);
return frmProgressBar;
}
}
}
I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.
Below are three different ways you can achieve what you want:
1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()
class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public void Test()
{
MethodUsingResetEvents();
}
private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}
private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}
2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx
class LockingWithAwait
{
public void Test()
{
DoSomething();
}
private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}
private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).
If you're using .NET 4.0 (with VS2012) or above, you can do this quite easily with the Task Parallel Library and async-await:
private async void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = await Task.Run(() =>
PullMSI.ExtractByMSIName("products.txt", false));
MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString());
}
For .NET 4, you'll need to add Microsoft.Bcl.Async.
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();
}
}