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();
}
}
Related
I have custom thread which parses WiFi networks and updates the UI (DataGridView and graphs). Here is the thread method:
private void RefreshThread()
{
var watch = Stopwatch.StartNew();
while (true)
{
UpdateAllNetworks();
UpdateAllInterferences();
UpdateAllColors();
switch (ActivePage)
{
case Page.Start:
break;
case Page.Networks:
this.Invoke((MethodInvoker)delegate
{
UpdateDataGridWithNetworks();
ClearGraphs();
Draw24GHzGraph();
DrawSignalsOverTimeGraph();
});
break;
case Page.Channels:
break;
case Page.Analyze:
break;
default:
break;
}
watch.Stop();
int elapsedMs = (int) watch.ElapsedMilliseconds;
if (elapsedMs < Constants.NetworksRefreshThreadInterval)
Thread.Sleep(Constants.NetworksRefreshThreadInterval - elapsedMs);
}
}
Custom DataGridView:
public class CustomDataGridView : DataGridView
{
...
protected override void OnCellClick(DataGridViewCellEventArgs e)
{
base.OnCellClick(e);
int Index = e.RowIndex;
if (Index != -1)
{
DataGridViewRow row = Rows[Index];
PrimaryKeyForSelectedRow = row.Cells[KeyName].Value.ToString();
}
}
}
The DataGridView is my custom DataGrid where I have a click event handler. I have observed that sometimes the event handler isn't called but in most cases it is.
What could be the problem? Is it related to multithreading or the event isn't queued?
Your code blocks main thread, use separate thread for your network details update. Here is quick sample how it done.
class Program
{
static void Main(string[] args)
{
var helper = new Looper(5000, YourMethod_RefreshThread);
helper.Start();
}
private static void YourMethod_RefreshThread()
{
Console.WriteLine(DateTime.Now);
}
}
public class Looper
{
private readonly Action _callback;
private readonly int _interval;
public Looper(int interval, Action callback)
{
if(interval <=0)
{
throw new ArgumentOutOfRangeException("interval");
}
if(callback == null)
{
throw new ArgumentNullException("callback");
}
_interval = interval;
_callback = callback;
}
private void Work()
{
var next = Environment.TickCount;
do
{
if (Environment.TickCount >= next)
{
_callback();
next = Environment.TickCount + _interval;
}
Thread.Sleep(_interval);
} while (IsRunning);
}
public void Start()
{
if (IsRunning)
{
return;
}
var thread = new Thread(Work);
thread.Start();
IsRunning = true;
}
public void Stop()
{
this.IsRunning = false;
}
public bool IsRunning { get; private set; }
I have written a simple Producer-Consumer queue. It keeps throwing an error that the safe handle is disposed.
namespace ConsoleApplication1
{
public class ProducerConsumer:IDisposable
{
private Queue<string> tasks = new Queue<string>();
Thread work;
readonly object obj=new object();
EventWaitHandle wh = new AutoResetEvent(true);
public ProducerConsumer()
{
work = new Thread(doWork);
work.Start();
}
public void doWork()
{
while (true)
{
string task = null;
wh.WaitOne();
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
}
}
}
public void ShutDown()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void Dispose()
{
tasks.Enqueue(null);
wh.Close();
work.Join();
}
public void AddTask(string str)
{
lock(obj){tasks.Enqueue(str);wh.Set();}
}
}
class Program
{
static EventWaitHandle _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
ProducerConsumer prod = new ProducerConsumer();
prod.AddTask("Started");
for (int i = 0; i < 5; i++) prod.AddTask(Convert.ToString(i));
prod.AddTask("Done");
prod.ShutDown();
Console.ReadLine();
}
}
}
If I change the wh.WaitOne() to the else part then it starts working
public void doWork()
{
while (true)
{
string task = null;
lock (obj){
if (tasks.Count > 0)
{
task = tasks.Dequeue();
if (task == null) return;
else
{
Console.WriteLine("Performing task: " + task);
Thread.Sleep(1000); // simulate work...
}
}
else
wh.WaitOne();
}
}
}
Can somebody throw a light on why this is happening?
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();
}
I'm trying to run the timer tick event which initially runs on my windows form,also when loaded on another class using a thread. I tried calling the timer event on another thread it didn't help. Am I supposed to use the same timer or create a new timer for that thread. This is my current implementation:
namespace XT_3_Sample_Application
{
public partial class Form1 : Form
{
Queue<string> receivedDataList = new Queue<string>();
System.Timers.Timer tmrTcpPolling = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
TM702_G2_Connection_Initialization();
}
static void threadcal()
{
Class1 c = new Class1();
c.timer_start();
c.test("192.168.1.188",9999);
}
public string Connection_Connect(string TCP_IP_Address, int TCP_Port_Number)
{
if (tcpClient.Connected)
{
Socket_Client = tcpClient.Client;
TcpStreamReader_Client = new StreamReader(tcpClient.GetStream(), Encoding.ASCII);
tmrTcpPolling.Start();
Status = "Connected\r\n";
}
else
{
Status = "Cannot Connect\r\n";
}
}
public string Connection_Disconnect()
{
tmrTcpPolling.Stop();
// do something
return "Disconnected\r\n";
}
void TM702_G2_Connection_Initialization()
{
tmrTcpPolling.Interval = 1;
tmrTcpPolling.Elapsed += new ElapsedEventHandler(tmrTcpPolling_Elapsed);
}
#region Timer Event
void tmrTcpPolling_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
if (tcpClient.Available > 0)
{
receivedDataList.Enqueue(TcpStreamReader_Client.ReadLine());
}
}
catch (Exception)
{
//throw;
}
}
private void tmrDisplay_Tick(object sender, EventArgs e)
{
Tick();
}
public void Tick()
{
Console.Write("tick" + Environment.NewLine);
if (receivedDataList.Count > 0)
{
string RAW_Str = receivedDataList.Dequeue();
//tbxConsoleOutput.AppendText(RAW_Str + Environment.NewLine);
tbxConsoleOutput.AppendText(Parser_Selection(RAW_Str) + Environment.NewLine);
}
}
#endregion
private void btnConnect_Click(object sender, EventArgs e)
{
tbxConsoleOutput.AppendText(Connection_Connect(tbxTCPIP.Text, Convert.ToInt32(tbxPort.Text, 10)));
Thread t = new Thread(threadcal);
t.Start();
}
}
}
But the timer tick event starts the moment the application is launched but not on button click - private void btnConnect_Click(object sender, EventArgs e).
I'm trying to call a separate thread for the class Class1's test method. I'm trying to use a similar timer event to receive output from the server, for this thread.
namespace XT_3_Sample_Application
{
class Class1
{
TcpClient tcpClient;
Socket Socket_Client;
StreamReader TcpStreamReader_Client; // Read in ASCII
Queue<string> receivedDataList = new Queue<string>();
System.Timers.Timer tmrTcpPolling = new System.Timers.Timer();
void TM702_G2_Connection_Initialization()
{
tmrTcpPolling.Interval = 1;
tmrTcpPolling.Elapsed += new ElapsedEventHandler(tmrTcpPolling_Elapsed);
}
public void test(string TCP_IP_Address, int TCP_Port_Number)
{
TM702_G2_Connection_Initialization();
try
{
string Status = "";
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
PingReply reply = pingSender.Send(TCP_IP_Address, timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
tcpClient = new TcpClient();
tcpClient.Connect(TCP_IP_Address, TCP_Port_Number);
if (tcpClient.Connected)
{
Socket_Client = tcpClient.Client;
TcpStreamReader_Client = new StreamReader(tcpClient.GetStream(), Encoding.ASCII);
tmrTcpPolling.Start();
Status = "Connected\r\n";
}
else
{
Status = "Cannot Connect\r\n";
}
}
else
{
Status = "Ping Fail\r\n";
}
MessageBox.Show(TCP_IP_Address + " :" + Status);
}
catch (System.Exception ex)
{
MessageBox.Show(TCP_IP_Address + " :" + ex.Message);
}
setFilterType();
setButtonRadioLvl();
heloCmd();
}
public void timer_Start()
{
Form1 f = new Form1();
f.Tick();
}
}
}
When tried the above method the timer is not fired on the new thread. Any suggestions on this?
Without any blocking code or loop your thread will not live long. the following calls your test method every one second and doesn't use timer
static void threadcal()
{
while (true)
{
Thread.Sleep(1000);
Class1 c = new Class1();
c.test("192.168.1.188", 9999);
}
}
I'm attempting to make my simple C# graphics library multi-threaded. However, after the introduction of this code:
/* foreach (IAffector affector in affectorLookup.Values)
affector.Update(timestep); */
taskManager.Value = timestep; taskManager.Start();
foreach (IAffector affector in affectorLookup.Values)
taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();
the simulation starts experiencing sharp lag-spikes, which seem to originate in TaskHandler.Run (I can't tell for sure, because adding the previous code makes my code profiler ignore anything outside TaskHandler.Run).
The task manager:
public class TaskManager
{
public delegate void MethodDel(float timestep);
private Queue<MethodDel> queue;
private List<TaskHandler> handlers;
private float value;
public float Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
public TaskManager()
{
this.queue = new Queue<MethodDel>();
this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);
for (int t = 0; t < this.handlers.Capacity; ++t)
this.handlers.Add(new TaskHandler(this));
this.value = 0;
}
public void Start()
{
foreach (var handler in handlers)
handler.Start();
}
public void Stop()
{
lock (queue)
queue.Clear();
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void StopWhenDone()
{
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void AddToQueue(MethodDel method)
{
lock (queue)
queue.Enqueue(method);
}
public bool GetFromQueue(out MethodDel method)
{
lock (queue)
{
if (queue.Count == 0) { method = null; return false; }
method = queue.Dequeue();
return true;
}
}
public int GetQueueCount()
{
return queue.Count;
}
internal void Wait()
{
// Have to wait for them one at a time because the main thread is STA.
WaitHandle[] waitHandles = new WaitHandle[1];
// for (int t = 0; t < handlers.Count; ++t)
// waitHandles[t] = handlers[t].WaitHandle;
// WaitHandle.WaitAll(waitHandles);
for (int t = 0; t < handlers.Count; ++t)
{ waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
}
}
And the task handler:
public class TaskHandler
{
private TaskManager manager;
private Thread thread;
private bool stopWhenDone;
private ManualResetEvent waitHandle;
public ManualResetEvent WaitHandle
{
get
{
return waitHandle;
}
}
public TaskHandler(TaskManager manager)
{
this.manager = manager;
}
public void Start()
{
waitHandle = new ManualResetEvent(false);
stopWhenDone = false;
thread = new Thread(Run);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
}
public void StopWhenDone()
{
this.stopWhenDone = true;
}
// Possible source of slowdown
private void Run()
{
TaskManager.MethodDel curMethod;
while (!stopWhenDone || manager.GetQueueCount() > 0)
{
if (manager.GetFromQueue(out curMethod))
{
curMethod(manager.Value);
}
}
waitHandle.Set();
}
}
Starting a thread is a heavy operation. Not sure if it's as heavy as you are experiencing, but that could be it. Also, having all your processing run parallel can be putting a big strain on your system with possibly little benefit...
I'm going to venture that the spikes have something to do with waitHandle.Set();
I like the overall design, but I have not used WaitHandle before, so I am unsure how this interacts with your design.