Help with synchronization from a unit test in C# - c#

I'm testing a class that wraps BackgroundWorker to perform an operation away from the UI thread in my application.
The test below fails if Timeout is exceeded and passes if progressEventCount reaches the expected number of events before then.
My question is about synchronization. asyncExecutor.Progressed is fired from the Thread Pool thread that BackgroundWorker is using and the test thread reads it back in the while loop.
Am I using lock correctly?
[Test]
[Timeout(1250)]
public void Execute()
{
var locker = new object();
const int numberOfEvents = 10;
const int frequencyOfEvents = 100;
var start = DateTime.Now;
int progressEventCount = 0;
IGradualOperation tester = new TestGradualOperation(numberOfEvents, frequencyOfEvents);
var asyncExecutor = new AsynchronousOperationExecutor();
asyncExecutor.Progressed += (s, e) => { lock (locker) progressEventCount++; };
asyncExecutor.Execute(tester);
while (true)
{
int count;
lock (locker)
{
count = progressEventCount;
}
if (count < numberOfEvents) continue;
Assert.Pass("Succeeded after {0} milliseconds", (DateTime.Now - start).TotalMilliseconds);
}
}
// Implementation
public class AsynchronousOperationExecutor
{
public void Execute(IGradualOperation gradualOperation)
{
var backgroundWorker = new BackgroundWorker {WorkerReportsProgress = true};
backgroundWorker.DoWork += BackgroundWorkerDoWork;
backgroundWorker.ProgressChanged += BackgroundWorkerProgressChanged;
backgroundWorker.RunWorkerAsync(gradualOperation);
}
private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
var myArgs = e.UserState as ProgressEventArgs;
OnProgressed(myArgs);
}
static void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var workerThis = sender as BackgroundWorker;
var operation = e.Argument as IGradualOperation;
if (workerThis == null || operation == null) return;
operation.Progressed += (s, e1) => workerThis.ReportProgress((int)e1.Percentage, e1);
operation.Run();
}
private void OnProgressed(ProgressEventArgs e)
{
if (Progressed != null)
Progressed(this, e);
}
public event EventHandler<ProgressEventArgs> Progressed;
}
// Test Helper Class
public class TestGradualOperation : IGradualOperation
{
private readonly int _numberOfEvents;
private readonly int _frequencyMilliseconds;
public TestGradualOperation(int numberOfEvents, int frequencyMilliseconds)
{
_numberOfEvents = numberOfEvents;
_frequencyMilliseconds = frequencyMilliseconds;
}
public void Run()
{
for (int i = 0; i < _numberOfEvents; i++)
{
Thread.Sleep(_frequencyMilliseconds);
OnProgressed(new ProgressEventArgs(i, _numberOfEvents));
}
}
private void OnProgressed(ProgressEventArgs e)
{
if (Progressed != null)
Progressed(this, e);
}
public event EventHandler<ProgressEventArgs> Progressed;
}

I think this revision is an improvement, blocking the test thread and signalling with an AutoResetEvent. Not winning any brownie points for test readability though.
[Test]
[Timeout(1250)]
public void Execute()
{
var locker = new object();
EventWaitHandle waitHandle = new AutoResetEvent(false);// <--
const int numberOfEvents = 10;
const int frequencyOfEvents = 100;
var start = DateTime.Now;
int progressEventCount = 0;
IGradualOperation tester = new TestGradualOperation(numberOfEvents, frequencyOfEvents);
var asyncExecutor = new AsynchronousOperationExecutor();
asyncExecutor.Progressed += (s, e) =>
{
lock (locker)
{
progressEventCount++;
waitHandle.Set();// <--
}
};
asyncExecutor.Execute(tester);
while (true)
{
waitHandle.WaitOne();// <--
if (progressEventCount < numberOfEvents) continue;
Assert.Pass("Succeeded after {0} milliseconds", (DateTime.Now - start).TotalMilliseconds);
}
}

Related

How to return to main when timer stopped from other class in c# console application

I have a class with timer like below
public class helper
{
Timer timer = new Timer();
private int counter = 0;
private int returnCode = 0;
public int Process()
{
SetTimer();
Console.WriteLine("The application started ");
return counter;
}
public void SetTimer()
{
int optionalWay = 0;
// Create a timer with a two second interval.
timer = new System.Timers.Timer(2000);
// Hook up the Elapsed event for the timer.
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, optionalWay);
timer.AutoReset = true;
timer.Enabled = true;
}
private void OnTimedEvent(Object source, ElapsedEventArgs e, int optionalWay)
{
counter++;
Console.WriteLine("Timer is ticking");
if (counter == 10)
{
timer.Stop();
timer.Dispose();
returnCode = returnCode + 1;
}
}
}
I have main function like this below
public static void Main()
{
helper helper = new helper();
int code = helper.Process();
Console.WriteLine("Main " + code.ToString());
Console.ReadLine();
}
what I want to do is return to main when my timer is stopped, not before that
, my timer class is running fine, main is getting printed like below
So main should wait till the result from timer is 1. And then end process
The code is working as it should. There is nothing inside the helper.Process() function that can wait or block the execution, so the function is returning immediately to the main before the OnTimedEvent is even executed.
A workaround can be done by implementing an event in the helper class and raise that event after the timer completes its work. And the main can listen to that event and act accordingly.
public class helper
{
Timer timer = new Timer();
private int counter = 0;
private int returnCode = 0;
public event EventHandler<int> Done;
...
private void OnTimedEvent(Object source, ElapsedEventArgs e, int optionalWay)
{
counter++;
Console.WriteLine("Timer is ticking");
if (counter == 10)
{
timer.Stop();
timer.Dispose();
returnCode = returnCode + 1;
if (Done != null)
{
Done.Invoke(this, returnCode);
}
}
}
}
And in the Program.cs
static void Main(string[] args)
{
helper helper = new helper();
helper.Done += helper_Done;
helper.Process();
Console.ReadLine();
}
static void helper_Done(object sender, int e)
{
Console.WriteLine("Main " + e.ToString());
}
Update
The Timer class uses a new thread from ThreadPool to execute the Elapsed event handler. So it cannot return to the Main which is running on a different thread. In short: what you are trying to do cannot not be achieved with a Timer.
Here is another solution using Thread.Sleep() which will satisfy your requirement, but keep in mind using Thread.Sleep() like this is not recommended.
public class helper
{
private int counter = 0;
private int returnCode = 0;
public int Process()
{
Console.WriteLine("The application started ");
StartTimer(2000);
return returnCode;
}
private void StartTimer(int ms)
{
while (counter++ < 10)
{
System.Threading.Thread.Sleep(ms);
Console.WriteLine("Timer is ticking");
}
returnCode = returnCode + 1;
}
}
class Program
{
static void Main(string[] args)
{
helper helper = new helper();
int code = helper.Process();
Console.WriteLine("Main " + code.ToString());
Console.ReadLine();
}
}
Again, this is NOT a good practice to use Thread.Sleep for a delayed execution and Thread.Sleep is less accurate compare to Timer.Elapsed. Try to change the design of your application and use Event or Callback function.
Change the Process function of helper class to accept a callback:
public void Process(Action<int> callBack)
{
SetTimer();
Console.WriteLine("The application started ");
if (timer != null)
timer.Disposed += (o, e) => callBack(counter);
}
Change the main function to send the callback:
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
helper helper = new helper();
helper.Process(c => Console.WriteLine("Main " + c.ToString()));
Console.ReadLine();
}
I have two options in mind, one of them and the ugliest, is to loop until timer is stopped, basically doing so:
public class helper
{
Timer timer = new Timer();
private int counter = 0;
private int returnCode = 0;
private bool timerWorking = false;
public int Process()
{
SetTimer();
Console.WriteLine("The application started ");
while(timerWorking){}
return counter;
}
public void SetTimer()
{
// All the staff you already have
timerWorking = true;
}
private void OnTimedEvent(Object source, ElapsedEventArgs e, int optionalWay)
{
counter++;
Console.WriteLine("Timer is ticking");
if (counter == 10)
{
//All the staff you already have
timerWorking = false;
}
}
}
Or, the more elegant, passing or registering a callback to be executed once the ending point is reached:
public class helper
{
Timer timer = new Timer();
private int counter = 0;
private int returnCode = 0;
Action<int> _doAfterTimerEnds
public void Process(Action<int> doAfterTimerEnds)
{
SetTimer();
_doAfterTimerEnds = doAfterTimerEnds;
Console.WriteLine("The application started ");
}
public void SetTimer()
{
int optionalWay = 0;
// Create a timer with a two second interval.
timer = new System.Timers.Timer(2000);
// Hook up the Elapsed event for the timer.
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, optionalWay);
timer.AutoReset = true;
timer.Enabled = true;
}
private void OnTimedEvent(Object source, ElapsedEventArgs e, int optionalWay)
{
counter++;
Console.WriteLine("Timer is ticking");
if (counter == 10)
{
timer.Stop();
timer.Dispose();
returnCode = returnCode + 1;
_doAfterTimerEnds(returnCode)
}
}
}
public static void Main()
{
var returnCode = 0;
var helper = new helper();
helper.Process(code => returnCode = code);
while (returnCode != 1) {}
Console.WriteLine("Main " + returnCode);
Console.ReadLine();
}
UPDATE: I've tested this last version and it is working as expected.

How to control Thread because it work incorrect?

My problem is:
I want to run through all ListStudent; it has five rows.
I am using for() to loop through all element in ListStudent and start thread corresponding.
It must run StartProcess(dtStudent.Rows[0].ToString(), 1); to ``StartProcess(dtStudent.Rows[5].ToString(), 1);`
But when I tried to debug, at rows while (run_process[idxThread].bwIsRun == true) - variable idxThread always is 5.
And I want it only run first thread because I will input to a program. After, when I click again btnProcess it will continue to next dtStudent.Rows.
Have any method to do this? Thanks..
I don't have to know an issue with my code.
This all my code to do this:
LThread[] run_process =new LThread[0];
int num_process = 0;
public void btnProcess()
{
DataTable dtStudent = mysql_db.ExcelLoad("ListStudent");
int total_row_student = dtStudent.Rows.Count; // 5 rows
if (num_process != total_row_student)
{
run_process = new LThread[total_row_student];
for (int idx = 0; idx < total_row_student; idx++)
{
run_process[idx] = new LThread();
run_process[idx].StartedEvent += new LThread.startDelegate(delegate (string arg)
{
StartProcess(dtStudent.Rows[idx - 1]["number"].ToString(), idx - 1);
});
}
}
num_process = total_row_student;
if (num_process == 0)
return;
flag = true;
Start_all_thread();
}
private void Start_all_thread()
{
for (int i = 0; i < run_process.Length; i++)
if (run_process[i] != null)
run_process[i].Start();
}
private void Stop_all_thread()
{
for (int i = 0; i < run_process.Length; i++)
if (run_process[i] != null)
run_process[i].Stop();
}
private void StartProcess(string output, int idxThread)
{
while (run_process[idxThread].bwIsRun == true)
{
if (flag == false)
continue;
// some code at here
Stop_all_thread();
}
}
And class LThread.cs is define:
public class LThread2
{
public delegate void startDelegate(string ID);
public event startDelegate StartedEvent;
public Boolean bwIsRun;
MicroTimer microTimer = new MicroTimer();
public LThread2()
{
microTimer.MicroTimerElapsed +=
new MicroTimer.MicroTimerElapsedEventHandler(OnTimedEvent);
microTimer.Interval = 2000;
}
private static int RandNumber(int Low, int High)
{
Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
int rnd = rndNum.Next(Low, High);
return rnd;
}
public void Start()
{
try
{
bwIsRun = true;
microTimer.Enabled = true;
}
catch { }
}
public void Stop()
{
try
{
bwIsRun = false;
microTimer.Enabled = false;
}
catch { }
}
private void OnTimedEvent(object sender,MicroTimerEventArgs timerEventArgs)
{
StartedEvent(RandNumber(100, 10000).ToString());
}
}
/// <summary>
/// Class emulates long process which runs in worker thread
/// and makes synchronous user UI operations.
/// </summary>
public class LThread : BackgroundWorker
{
#region Members
public delegate void startDelegate(string ID);
public event startDelegate StartedEvent;
private static int RandNumber(int Low, int High)
{
Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), System.Globalization.NumberStyles.HexNumber));
int rnd = rndNum.Next(Low, High);
return rnd;
}
protected override void OnDoWork(DoWorkEventArgs e)
{
StartedEvent(RandNumber(100,10000).ToString()); //put whatever parameter suits you or nothing
base.OnDoWork(e);
e.Result = e.Argument;
}
BackgroundWorker bwThread;
// Main thread sets this event to stop worker thread:
public Boolean bwIsRun;
int m_time_delay = 10000;
Delegate m_form_method_run;
Delegate m_form_method_stop;
Form m_type_form;
#endregion
#region Functions
public void Start()
{
try
{
bwIsRun = true;
this.RunWorkerAsync();
}
catch { }
}
public void Stop()
{
try
{
bwIsRun = false;
}
catch { }
}
private void StartToListen(object sender, DoWorkEventArgs e)
{
while (true)
{
Thread.Sleep(m_time_delay);
if (bwIsRun == true)
{
m_type_form.Invoke(m_form_method_run);
}
else
{
BackgroundWorker bwAsync = sender as BackgroundWorker;
if (bwAsync.CancellationPending)
{
e.Cancel = true;
return;
}
break;
}
}
}
#endregion
}
You issue is probably in the for (int idx = 0; idx < total_row_student; idx++) for-loop. You're starting threads that will start long after the loop is finished so the variable idx is already at 5 before any of the threads actually start.
Try changing the code to this to fix:
for (int idx = 0; idx < total_row_student; idx++)
{
int local_idx = idx;
run_process[idx] = new LThread();
run_process[idx].StartedEvent += new LThread.startDelegate(delegate (string arg)
{
StartProcess(dtStudent.Rows[local_idx - 1]["number"].ToString(), local_idx - 1);
});
}

Cancel Task inside task when timed out. C#

I'm trying to run collection of tasks. I have a procesing object model, which properties I don't know. That's is why I've created a child of this class. I have a collection of ProcesingTask objects. This is my code:
public sealed class ProcessingTask : ProcessingObject
{
private CancellationTokenSource cancelToken;
private System.Timers.Timer _timer;
public int TimeOut {private get; set; }
public int ProcessObjectID { get; private set; }
public Task ProcessObjectTask { get; private set; }
public QueueObject queueObject { private get; set; }
public ProcessingTask(int processObjectID)
{
this.ProcessObjectID = processObjectID;
ResetTask();
}
private void InitialTimeOut()
{
_timer = new System.Timers.Timer(TimeOut);
_timer.Elapsed += new ElapsedEventHandler(TimedOut);
_timer.Enabled = true;
}
private void TimedOut(object sender, ElapsedEventArgs e)
{
cancelToken.Cancel();
Console.WriteLine("Thread {0} was timed out...", ProcessObjectID);
_timer.Stop();
}
public void ResetTask()
{
cancelToken = new CancellationTokenSource();
ProcessObjectTask = new Task(() => DoTaskWork(), cancelToken.Token);
}
public void DoTaskWork()
{
InitialTimeOut();
Random rand = new Random();
int operationTime = rand.Next(2000, 20000);
Thread.Sleep(operationTime);
_timer.Stop();
Console.WriteLine("Thread {0} was finished...", ProcessObjectID);
}
}
public class CustomThreadPool
{
private IList<ProcessingTask> _processingTasks;
public CustomThreadPool(List<ProcessingTask> processingObjects)
{
this._processingTasks = processingObjects;
}
public void RunThreadPool(Queue<QueueObject> queue, int batchSize)
{
for (int i = 1; i <= batchSize; i++)
{
QueueObject queueObject = queue.ToArray().ToList().FirstOrDefault();
ProcessingTask task = _processingTasks.FirstOrDefault(x => x.ProcessObjectID == queueObject.QueueObjectId);
task.queueObject = queue.Dequeue();
task.TimeOut = 3000;
task.ProcessObjectTask.Start();
}
}
public void WaitAll()
{
var tasks = _processingTasks.Select(x => x.ProcessObjectTask).ToArray();
Task.WaitAll(tasks);
}
}
I need to stop DoTaskWork() if running time was timed out. I'm trying to use timer and CancellationToken. But DoTaskWork() still doing his job after TimedOut(). Is any way to resolve this issue?
Though you send the cancel signal, you don't do anything with that in DoTaskWork
public void DoTaskWork()
{
InitialTimeOut();
Random rand = new Random();
int operationTime = rand.Next(2000, 20000);
// Thread.Sleep(operationTime); // this imitates a non-responsive operation
// but this imitates a responsive operation:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (!cancelToken.IsCancellationRequested
&& stopwatch.ElapsedMilliseconds < operationTime)
{
Thread.Sleep(10);
}
_timer.Stop();
Console.WriteLine("Thread {0} was finished...", ProcessObjectID);
}
You have to implement your method DoTaskWork() accordingly.
Assume this as a sample. I am assuming that your thead is doing some continuos work rather just sleep. Otherwise you can use abort method which will just abort the thread even if its in sleep mode.
public void DoTaskWork()
{
InitialTimeOut();
Random rand = new Random();
int operationTime = rand.Next(2000, 20000);
while (true)
{
if (cancelToken.IsCancellationRequested)
{
throw new Exception("Cancellation requested.");
}
Thread.Sleep(operationTime);
}
_timer.Stop();
Console.WriteLine("Thread {0} was finished...", ProcessObjectID);
}

refreshing UI elements in work threads

Question 1: I want to refresh a label by a work thread by delegate and invoke. It works well until i try to close the form. In closing event, I stop the work thread and then the UI thread while an exception occurs(Object disposed exception). It seems that form1 is disposed. I don't know what's wrong.
Question 2: When running this code, the memory usage keep increasing. I don't think it should take so much memory space. You can see find this by checking the task manager.
here's my code:
(.Net Framework 4, winforms)
Scheduler:
class Scheduler
{
private Thread[] workThreads = null;
//Scheduler started flag
public static bool bSchedulerStarted = false;
//Work threads stop event
public static EventWaitHandle stopWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
private static Scheduler self = null;
public static Scheduler getInstance()
{
if (self == null)
{
self = new Scheduler();
}
return self;
}
private Scheduler()
{
workThreads = new Thread[1];
}
private void CreateThread()
{
workThreads[0] = new Thread(Worker.doWork);
workThreads[0].IsBackground = true;
}
public void startUp()
{
if (!bSchedulerStarted)
{
stopWaitHandle.Reset();
CreateThread();
//Start all work threads
for (int i = 0; i < 1; i++)
{
workThreads[i].Start();
}
bSchedulerStarted = true;
}
}
public void stop()
{
if (!bSchedulerStarted)
return;
//Send stop event
stopWaitHandle.Set();
bSchedulerStarted = false;
if (workThreads != null)
{
//wait for all work threads to stop
for (int i = 0; i <1; i++)
{
if (workThreads[i] != null && workThreads[i].IsAlive)
workThreads[i].Join();
}
}
}
}
Worker:
class Worker
{
public static void doWork()
{
while (true)
{
if (Scheduler.stopWaitHandle.WaitOne(10, false) == true)
{
break;
}
Form1.count++;
Form1.sysMsgEvent.Set();
Thread.Sleep(10);
}
}
}
And form:
public partial class Form1 : Form
{
public static int count = 0;
public static EventWaitHandle sysMsgEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
public static EventWaitHandle stopEvent = new EventWaitHandle(false, EventResetMode.ManualReset);
private EventWaitHandle[] waitEvent = null;
Thread UIThread = null;
private delegate void ShowMsg();
public Form1()
{
InitializeComponent();
waitEvent = new EventWaitHandle[2];
waitEvent[0] = stopEvent;
waitEvent[1] = sysMsgEvent;
}
public void UpdateUI()
{
while (true)
{
switch (EventWaitHandle.WaitAny(waitEvent))
{
case 0: //Stop UI thread
return;
case 1: //Refresh UI elements
updateLabel();
break;
default:
return;
}//switch
}//while
}
private void updateLabel()
{
if (label1.InvokeRequired)
{
ShowMsg d = new ShowMsg(updateLabel);
this.Invoke(d, null);
}
else
{
label1.Text = count.ToString();
}
}
private void Form1_Load(object sender, EventArgs e)
{
UIThread = new Thread(new ThreadStart(UpdateUI));
UIThread.Start();
Scheduler sc = Scheduler.getInstance();
sc.startUp();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Scheduler sc = Scheduler.getInstance();
sc.stop();
//stop UI thread
Form1.stopEvent.Set();
}
}

Signaling threads to stop

have such code.
Start threads:
Thread[] thr;
static object locker = new object();
bool liking = true;
private void button2_Click(object sender, EventArgs e)
{
button2.Enabled = false;
button3.Enabled = true;
string post = create_note();
decimal value = Program.Data.numericUpDown1;
int i = 0;
int j = (int)(value);
thr = new Thread[j];
for (; i < j; i++)
{
thr[i] = new Thread(() => invite(post));
thr[i].IsBackground = true;
thr[i].Start();
}
}
public void invite(string post)
{
while (liking)
{
if (//some comdition)
exit all threads, and start string post = create_note(); again
}
}
If some condition in invite(string post) comes true I need to stop all threads, and go to string post = create_note(); again, get string post and start threads again.
How to do it?
Instead of manual thread management, use Parallel.For with CancellationToken:
var cts = new CancellationTokenSource();
var options = new ParallelOptions
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = System.Environment.ProcessorCount
};
var result = Parallel.For(0, j, options, i =>
{
invite(post);
options.CancellationToken.ThrowIfCancellationRequested();
});
When you want to cancel parallel calculations, just call cts.Cancel() from external code.
You can use lock and create a class that manage your threads like that :
public class SyncClass
{
public Thread[] thr;
private int NumberOfWorkingThreads { get; set; }
private object Sync = new object();
public int ThreadNumber { get; private set; }
public event EventHandler TasksFinished;
public SyncClass(int threadNumber)
{
thr = new Thread[threadNumber];
ThreadNumber = threadNumber;
NumberOfWorkingThreads = ThreadNumber;
//LunchThreads(threadNumber);
}
protected void OnTasksFinished()
{
if (TasksFinished == null)
return;
lock (Sync)
{
NumberOfWorkingThreads--;
if (NumberOfWorkingThreads == 0)
TasksFinished(this, new EventArgs());
}
}
public void LunchThreads()
{
string post = create_note();
for (int i = 0; i < ThreadNumber; i++)
{
thr[i] = new Thread(() => invite(post));
thr[i].IsBackground = true;
thr[i].Start();
}
}
private void invite(string post)
{
while (true)
{
if (true)
{
break;
}
}
OnTasksFinished();
}
}
Use the event to notify the end of all threads then the class will be used like that:
private void Operation()
{
var sync = new SyncClass(10);
sync.TasksFinished += sync_TasksFinished;
sync.LunchThreads();
}
void sync_TasksFinished(object sender, EventArgs e)
{
Operation();
}

Categories