How to control Thread because it work incorrect? - c#

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);
});
}

Related

Set maximum value for the progress bar to match length of queue.

I would like to visualize how a queue when accessed by two reader threads and a writer thread grows and shrinks, with help of a progressbar in mainform. I will use a delegate to invoke the progress bar in main form and set its value to Queue.Value ( Toys.lenght). The progressbar does not behave as stated, it does not grow all the way and the lenght variables does not either.
public class Buffer
{
private delegate void Display(int v, ProgressBar f );
private Queue<Toy> Toys = new Queue<Toy>();
private object MyLock = new object();
private int max;
private int lenght;
private ProgressBar progressbar1;
public Buffer(ProgressBar r)
{
this.progressbar1 = r;
this.max = 10;
this.lenght = Toys.Count;
}
public void writeMethod(Toy toy)
{
lock (MyLock)
{
if (Toys.Count == max)
{
Monitor.Wait(MyLock);
}
Toys.Enqueue(toy);
Monitor.PulseAll(MyLock);
progressbar1.Invoke(new Display(Disp), new object[] {Toys.Count, progressbar1});
MessageBox.Show("Que contains these items" + lenght);
}
}
public void readMethod()
{
lock (MyLock)
{
if(Toys.Count == 0)
{
Monitor.Wait(MyLock);
}
Toys.Dequeue();
Monitor.PulseAll(MyLock);
}
}
public void Disp(int I, ProgressBar l)
{
progressbar1.Value = I;
}
}
}
Double check the variable lenght, use count instead. Change Progressbar default setting for maximum size from 100 to 10 to match size of the queue Toys. In Disp method change from = I to += I;
{
public class Buffer
{
private delegate void Display(int v, ProgressBar f );
private Queue<Toy> Toys = new Queue<Toy>();
private object MyLock = new object();
private int max;
private int lenght;
private ProgressBar progressbar1;
public Buffer(ProgressBar r)
{
this.progressbar1 = r;
this.max = 10;
this.lenght = Toys.Count;
}
public void writeMethod(Toy toy)
{
lock (MyLock)
{
if (Toys.Count >= max)
{
Monitor.Wait(MyLock);
}
if(Toys.Count <= max)
{
Toys.Enqueue(toy);
progressbar1.Invoke(new Display(Disp), new object[] {Toys.Count, progressbar1});
}
Monitor.PulseAll(MyLock);
MessageBox.Show("Que contains these items" + Toys.Count);
}
}
public void readMethod()
{
lock (MyLock)
{
if(Toys.Count == 0)
{
Monitor.Wait(MyLock);
}
Toys.Dequeue();
Monitor.PulseAll(MyLock);
}
}
public void Disp(int I, ProgressBar l)
{
progressbar1.Value += I;
}
}
}

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();
}

WinFormApps vs. ConsoleApps : BackgroundWorker and EventWaitHandle

I have two programs : WinformApplication and ConsoleApplication with .NET 2.0 , C#, and VS2010.
Two programs has same taks with backgroundworker and EventWaitHandle.
Create BGW
DoWork
RunWorkerCompleted { .. event.Set(); .. }
Wait event.waitone()
Console apps works fine. However, Forms apps doesn't work. Here is full code. Can you try it and let me know what is the problem.
Here is WinFormApplication Code :
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
ProducerConsumerQueue[] q = new ProducerConsumerQueue[51];
public Form1()
{
InitializeComponent();
SetMinThreads();
for (int i = 0; i < 51; i++)
q[i] = new ProducerConsumerQueue(i);
ReadyToWork();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void SetMinThreads()
{
int workerT, portT;
int minRequire = 51 + 5;
ThreadPool.GetMaxThreads(out workerT, out portT);
ThreadPool.GetMinThreads(out workerT, out portT);
if (workerT < minRequire)
{
int inc = minRequire - workerT;
workerT += inc;
}
ThreadPool.SetMinThreads(workerT, portT);
Console.WriteLine(string.Format("Min: worker - {0}, port - {1}", workerT, portT));
}
private void ReadyToWork()
{
for (int i = 0; i < 51; i++)
q[i].ReadParamFromController(true);
for (int i = 0; i < 51; i++)
q[i].ReadParamFromController(false);
}
}
class ProducerConsumerQueue
{
EventWaitHandle _wh = new ManualResetEvent(false);
int _id;
public ProducerConsumerQueue(int id)
{
_id = id;
}
public void ReadParamFromController(bool isStart)
{
if (isStart)
{
{
_wh = new ManualResetEvent(false);
BackgroundWorker bwReadFromController = new BackgroundWorker();
bwReadFromController.WorkerSupportsCancellation = true;
bwReadFromController.DoWork += new DoWorkEventHandler(bwReadFromController_DoWork);
bwReadFromController.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwReadFromController_RunWorkerCompleted);
bwReadFromController.RunWorkerAsync();
}
}
else
{
_wh.WaitOne();
}
}
private void bwReadFromController_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
Console.WriteLine("Cancelled task: ");
}
else if (!(e.Error == null))
{
Console.WriteLine("Error task: ");
}
else
{
Console.WriteLine("Performing Done: " + _id);
}
_wh.Set();
((BackgroundWorker)sender).Dispose();
GC.Collect();
}
private void bwReadFromController_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
const int readFromControllerCount = 25;
for (int i = 0; i < readFromControllerCount; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Do something
System.Threading.Thread.Sleep(10);
}
}
}
}
}
Here is ConsoleApplication Code :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
DateTime dt = DateTime.Now;
Console.WriteLine(string.Format("start {0}", dt.ToString()));
SetMinThreads();
ProducerConsumerQueue[] q = new ProducerConsumerQueue[51];
for (int i = 0; i < 51; i++)
q[i] = new ProducerConsumerQueue(i);
for (int i = 0; i < 51; i++)
q[i].ReadParamFromController(true);// = new ProducerConsumerQueue();
dt = DateTime.Now;
Console.WriteLine(string.Format("Read Start {0}", dt.ToString()));
for (int i = 0; i < 51; i++)
q[i].ReadParamFromController(false);// = new ProducerConsumerQueue();
Console.WriteLine();
dt = DateTime.Now;
Console.WriteLine(string.Format("Workers complete! {0}", dt.ToString()));
// Exiting the using statement calls q's Dispose method, which
// enqueues a null task and waits until the consumer finishes.
Console.ReadLine();
}
static private void SetMinThreads()
{
int workerT, portT;
int minRequire = 51 + 5;
ThreadPool.GetMaxThreads(out workerT, out portT);
ThreadPool.GetMinThreads(out workerT, out portT);
if (workerT < minRequire)
{
int inc = minRequire - workerT;
workerT += inc;
}
ThreadPool.SetMinThreads(workerT, portT);
Console.WriteLine(string.Format("Min: worker - {0}, port - {1}", workerT, portT));
}
}
class ProducerConsumerQueue
{
EventWaitHandle _wh = new ManualResetEvent(false);
int _id;
public ProducerConsumerQueue(int id)
{
_id = id;
}
public void ReadParamFromController(bool isStart)
{
if (isStart)
{
{
_wh = new ManualResetEvent(false);
BackgroundWorker bwReadFromController = new BackgroundWorker();
bwReadFromController.WorkerSupportsCancellation = true;
bwReadFromController.DoWork += new DoWorkEventHandler(bwReadFromController_DoWork);
bwReadFromController.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwReadFromController_RunWorkerCompleted);
bwReadFromController.RunWorkerAsync();
}
}
else
{
_wh.WaitOne();
}
}
private void bwReadFromController_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
Console.WriteLine("Cancelled task: ");
}
else if (!(e.Error == null))
{
Console.WriteLine("Error task: ");
}
else
{
Console.WriteLine("Performing Done: " + _id);
}
_wh.Set();
((BackgroundWorker)sender).Dispose();
GC.Collect();
}
private void bwReadFromController_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
const int readFromControllerCount = 1;
for (int i = 0; i < readFromControllerCount; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
//Console.WriteLine("Performing tasks: " + i);
System.Threading.Thread.Sleep(10);
}
}
}
}
}

Help with synchronization from a unit test in 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);
}
}

Thread Pooling help

Having some issue with Threadpooling here that I need some help with please. I am trying to write a Generator, and I need to allow users generate up to 10,000 lines with the code below. Problem with this is the line
WaitHandle.WaitAll(doneEvents);
Can only handle 64 WaitAll at a time, How can I best apply thread pooling to my code in this case?
public void GenerateInsertStatements(int iRequiredRows)
{
// One event is used for each row object
ManualResetEvent[] doneEvents = new ManualResetEvent[iRequiredRows];
Row[] rows = new Row[iRequiredRows];
for (int i = 0; i < iRequiredRows; i++)
{
doneEvents[i] = new ManualResetEvent(false);
Row row = new Row(this.Name, this.TableColumns, doneEvents[i]);
rows[i] = row;
ThreadPool.QueueUserWorkItem(row.ThreadPoolCallback, i);
}
WaitHandle.WaitAll(doneEvents);
using (sr = new StreamWriter(this.Name + ".sql"))
{
for(int i=0; i<rows.Length; i++)
{
WriteStatementToFile(i, rows[i].GeneratedInsertStatement);
}
}
}
Thanks in advance
I would use just one WaitHandle and one int. Like:
int done_when_zero; // This is a field of the class
ManualResetEvent evt = new ManualResetEvent (false); // Field
...
done_when_zero = iRequiredRows; // This goes before the loop
...
evt.WaitOne (); // this goes after the loop
evt.Reset (); // Prepare for next execution if needed
And then, at the end of ThreadPoolCallback:
if (Interlocked.Decrement (ref done_when_zero)) <= 0)
evt.Set ();
As it was already suggested using a counter and a single ManualResetEvent should work fine for you. Below is ThreadPoolWait class taken from .NET Matters: ThreadPoolWait and HandleLeakTracer (see Figure 3 Better Implementation of ThreadPoolWait for more info)
public class ThreadPoolWait : IDisposable
{
private int _remainingWorkItems = 1;
private ManualResetEvent _done = new ManualResetEvent(false);
public void QueueUserWorkItem(WaitCallback callback)
{
QueueUserWorkItem(callback, null);
}
public void QueueUserWorkItem(WaitCallback callback, object state)
{
ThrowIfDisposed();
QueuedCallback qc = new QueuedCallback();
qc.Callback = callback;
qc.State = state;
lock (_done) _remainingWorkItems++;
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleWorkItem), qc);
}
public bool WaitOne() { return WaitOne(-1, false); }
public bool WaitOne(TimeSpan timeout, bool exitContext)
{
return WaitOne((int)timeout.TotalMilliseconds, exitContext);
}
public bool WaitOne(int millisecondsTimeout, bool exitContext)
{
ThrowIfDisposed();
DoneWorkItem();
bool rv = _done.WaitOne(millisecondsTimeout, exitContext);
lock (_done)
{
if (rv)
{
_remainingWorkItems = 1;
_done.Reset();
}
else _remainingWorkItems++;
}
return rv;
}
private void HandleWorkItem(object state)
{
QueuedCallback qc = (QueuedCallback)state;
try { qc.Callback(qc.State); }
finally { DoneWorkItem(); }
}
private void DoneWorkItem()
{
lock (_done)
{
--_remainingWorkItems;
if (_remainingWorkItems == 0) _done.Set();
}
}
private class QueuedCallback
{
public WaitCallback Callback;
public object State;
}
private void ThrowIfDisposed()
{
if (_done == null) throw new ObjectDisposedException(GetType().Name);
}
public void Dispose()
{
if (_done != null)
{
((IDisposable)_done).Dispose();
_done = null;
}
}
}
Probably not the most efficient solution, but it should work regardless of the 64 wait handles limit :
for(int i = 0; i < iRequiredRows; i++)
doneEvents[i].WaitOne();

Categories