Is there any possible way to access the field - str in the Class Program and the variable num - in the main function?
class Program
{
string str = "This is a string";
static void Main(string[] args)
{
int num = 100;
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
var timer = new System.Timers.Timer(10000);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
for (int i = 0; i < 20; i++)
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + "current I is " + i.ToString());
Thread.Sleep(1000);
}
Console.ReadLine();
}
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Debug.WriteLine(str);
Debug.WriteLine(num);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " current is timer");
//throw new NotImplementedException();
}
}
Best Regards,
The field just needs to be made static.
static string str = "This is a string";
To access num you need to use a lambda expression.
timer.Elapsed += new ElapsedEventHandler((s, e) =>
{
Debug.WriteLine(str);
Debug.WriteLine(num);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " current is timer");
});
You can also use an anonymous method.
timer.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs e)
{
Debug.WriteLine(str);
Debug.WriteLine(num);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " current is timer");
});
There is one more option. The System.Threading.Timer class allows you to pass a state object.
var timer = new System.Threading.Timer((state) =>
{
Debug.WriteLine(str);
Debug.WriteLine(state);
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " current is timer");
}, num, 10000, 10000);
str should be accessible directly if you change it to static as its implemented now since it is at class level. Num is internal to main and so cannot be accessed unless you pass a reference to it. You can move it external to main, or if its supported in ElapsedEventArgs pass a reference to it and retrieve it that way.
Related
I don't unrdersntat why with this code
static void Main(string[] args)
{
Console.WriteLine("START PROGRAMN-----------------------------------");
test();
Console.WriteLine("END PROGRAMN-----------------------------------");
Console.Read();
}
[ThreadStatic]
private static int i;
private static void test()
{
for (i = 0; i < 2; i++)
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += (sender, args) =>
{
Console.WriteLine("START Thread-------------");
Console.WriteLine("Print:" + i);
};
bw.RunWorkerCompleted += (sender, args) =>
{
Console.WriteLine("END Thread-------------");
if (args.Error != null)
{
Console.WriteLine(args.Error.ToString());
}
};
bw.RunWorkerAsync(); // starts the
}
}
It will show this in console:
START PROGRAMN-----------------------------------
END PROGRAMN-----------------------------------
START Thread-------------
Print:0
END Thread-------------
START Thread-------------
Print:0
END Thread-------------
Why second print doesn't show print 1?
I think the first iteration is correct because I see print: 0 but in second why I don't see print: 1?
EDIT FOR ANSER
Without [ThreadStatic]
static void Main(string[] args)
{
Console.WriteLine("START PROGRAMN-----------------------------------");
test();
Console.WriteLine("END PROGRAMN-----------------------------------");
Console.Read();
}
[ThreadStatic]
private static int i;
private static void test()
{
for (i = 0; i < 2; i++)
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += (sender, args) =>
{
Console.WriteLine("START Thread-------------");
Console.WriteLine("Print:" + i);
};
bw.RunWorkerCompleted += (sender, args) =>
{
Console.WriteLine("END Thread-------------");
if (args.Error != null)
{
Console.WriteLine(args.Error.ToString());
}
};
bw.RunWorkerAsync(); // starts the
}
}
It will show this in console:
START PROGRAMN-----------------------------------
END PROGRAMN-----------------------------------
START Thread-------------
Print:2
END Thread-------------
START Thread-------------
Print:2
END Thread-------------
Why first pirnt doesn't show print 0 and second print 1?
why show 2?
I think you do not really understand the ThreadStatic attribute here. It means that, by definition, Indicates that the value of a static field is unique for every thread. That means the value is unique for the Main Thread (where you are creating the BackgroundWorkers) and the BackgroundWorkers, that will always have the default value 0 for i.
Forget about that ThreadStatic you are not using this properly. It's not what you need in you case. The problem you are trying to bypass is because otherwise your result gets the latest value because the DoEvent is not started on the first thread that it already changed. You end up with a race condition. You do need to use arguments to have clear local instance of the variable. the easiest way is to change your code like so
for (int i = 0; i < 2; i++)
{
var bw = new BackgroundWorker();
// define the event handlers
bw.DoWork += (sender, args) =>
{
// get the argument
var value = args.Argument.ToString();
Console.WriteLine("START Thread-------------");
Console.WriteLine("Print:" + value);
};
bw.RunWorkerCompleted += (sender, args) =>
{
Console.WriteLine("END Thread-------------");
if (args.Error != null)
{
Console.WriteLine(args.Error.ToString());
}
};
bw.RunWorkerAsync(i); // starts the thread with arguments
}
Check your 'Console.WriteLine("Print:" + i);' inside the lambda.
The "Print:" + i is not evaluated and concatenated while inside the for loop. It will be present in your anonymous method and only will be concatenated when that method runs.
Because of your [ThreadStatic] attribute, int i is not shared between threads. "Each executing thread has a separate instance of the field, and independently sets and gets values for that field. If the field is accessed on a different thread, it will contain a different value."
int i will be instantiated and you get the int default value.
This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 6 years ago.
I have been learning before about threading but in C++. Now for the first time I'm trying to configure this code to work corectly but without CheckIllegal ... = false.
I have been trying to put delegates in there and lots of other stuff but the I getting the same problem. Both threads are entering the methof WriteInLog and I can't really see how to make this work.
Anyone has an idea and explanation?
namespace viseNitniRad
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int firstNum = 0;
private int secondNum = 0;
public Thread firstThread;
public Thread secondThread;
public void WriteInLog(string message)
{
lock (textBox3)
{
textBox3.Text += message + Environment.NewLine;
}
}
private void CheckInput()
{
int pom = 0;
firstNum = int.Parse(textBox1.Text);
secondNum = int.Parse(textBox2.Text);
if (firstNum > secondNum) {
pom = secondNum;
secondNum = firstNum;
firstNum = pom; }
WriteInLog("Prvi broj: " + firstNum.ToString());
WriteInLog("Drugi broj: " + secondNum.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
CheckInput();
}
public delegate void ThreadSum();
public delegate void ThreadUmn();
public void Threadsumm()
{
int suma = 0;
for (int i = firstNum; i < secondNum; i++)
suma += i;
WriteInLog("Suma= " + suma.ToString() + " kraj: " + DateTime.Now.ToString());
}
public void ThreadUmno()
{
int umnozak = 1;
for (int i = firstNum; i < secondNum; i++)
umnozak*= i;
WriteInLog("Umnozak= " + umnozak.ToString() + " kraj: " + DateTime.Now.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
WriteInLog("Pocetak svih izracuna u: " + DateTime.Now.ToString());
firstThread = new Thread(new ThreadStart(Threadsumm));
secondThread = new Thread(new ThreadStart(ThreadUmno));
firstThread.Start();
secondThread.Start();
}
}
}
To access control from non-UI thread you have to use Control.Invoke method https://msdn.microsoft.com/en-us/library/a1hetckb(v=vs.110).aspx
Instead of Threads you can use BackgroundWorker
https://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Also:
WinForm Multithreading. Use backgroundWorker or not?
If anyone comes here and want to know exactly the answer on this question, here it is.
I didn't want to change anything so I learn something about invoke. Furthermore, here's the 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.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace viseNitniRad
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int firstNum = 0;
private int secondNum = 0;
public Thread firstThread;
public Thread secondThread;
public void WriteInLog(string message)
{
if (this.textBox3.InvokeRequired)
{
ThreadSum ts = new ThreadSum(WriteInLog);
this.Invoke(ts, new object[] { message });
}
else
{
this.textBox3.Text += message + Environment.NewLine;
}
}
private void CheckInput()
{
int pom = 0;
firstNum = int.Parse(textBox1.Text);
secondNum = int.Parse(textBox2.Text);
if (firstNum > secondNum) {
pom = secondNum;
secondNum = firstNum;
firstNum = pom; }
WriteInLog("Prvi broj: " + firstNum.ToString());
WriteInLog("Drugi broj: " + secondNum.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
CheckInput();
}
public delegate void ThreadSum(string message);
public delegate void ThreadUmn();
public void Threadsumm()
{
int suma = 0;
for (int i = firstNum; i < secondNum; i++)
suma += i;
WriteInLog("Suma= " + suma.ToString() + " kraj: " + DateTime.Now.ToString());
}
public void ThreadUmno()
{
int umnozak = 1;
for (int i = firstNum; i < secondNum; i++)
umnozak*= i;
WriteInLog("Umnozak= " + umnozak.ToString() + " kraj: " + DateTime.Now.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
WriteInLog("Pocetak svih izracuna u: " + DateTime.Now.ToString());
firstThread = new Thread(new ThreadStart(Threadsumm));
secondThread = new Thread(new ThreadStart(ThreadUmno));
firstThread.Start();
secondThread.Start();
}
}
}
As you can see I made little change in function WriteInLog, where I deleted lock method and put an condition if invokeRequired to initialize Delegate to run the same method and than Invoke him, if not, just to update the same textbox3.
Thank you all! :)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
When I execute this code in command line, it's working fine:
class Program
{
private static List<Ping> pingers = new List<Ping>();
private static List<string> value = new List<string>();
private static int instances = 0;
private static object #lock = new object();
private static int result = 0;
private static int timeOut = 2500;
private static int ttl = 7;
public static void Main()
{
string baseIP = "192.168.1.";
Console.WriteLine("Pinging destinations of D-class in {0}*", baseIP);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
SpinWait wait = new SpinWait();
int cnt =1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeOut, data, po);
cnt += 1;
}
//while (instances > 0)
//{
// wait.SpinOnce();
//}
watch.Stop();
for (int i = 0; i < value.Count; i++)
{
Console.WriteLine(value[i]);
}
DestroyPingers();
Console.WriteLine("Finished in {0}. Found {1} active IP-addresses.", watch.Elapsed.ToString(), result);
Console.ReadKey();
}
public static void Ping_completed(object s, PingCompletedEventArgs e)
{
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string sa = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(sa);
//Console.WriteLine(sa);
String diachiip = e.Reply.Address.ToString();
result += 1;
}
else
{
//Console.WriteLine(String.Concat("Non-active IP: ", e.Reply.Address.ToString()))
}
}
private static void CreatePingers(int cnt)
{
for (int i = 1; i <= cnt; i++)
{
Ping p = new Ping();
p.PingCompleted += Ping_completed;
pingers.Add(p);
}
}
private static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed;
p.Dispose();
}
pingers.Clear();
}
}
But when I convert from it to window form, it doesn't work. I don't kwow why, I have tried many different ways...
Code is here:
public partial class Form1 : Form
{
public static List<Ping> pingers = new List<Ping>();
public static List<string> value = new List<string>();
public static int instances = 0;
public static object #lock = new object();
public static int result = 0;
public int timeout = 2500;
public static int ttl = 7;
public Form1()
{
InitializeComponent();
}
public void btnscan_Click(object sender, EventArgs e)
{
string baseIP = "192.168.1.";
//int kt = Int32.Parse(txtkt.Text);
//int start = Int32.Parse(txtstart.Text);
CreatePingers(254);
PingOptions po = new PingOptions(ttl, true);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
byte[] data = enc.GetBytes("");
int cnt = 1;
Stopwatch watch = Stopwatch.StartNew();
foreach (Ping p in pingers)
{
lock (#lock)
{
instances += 1;
}
p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data, po);
cnt += 1;
}
watch.Stop();
//Result alway return 0
lst1.Items.Add(result.ToString());
lst1.Items.Add(value.Count.ToString());
for (int i = 0; i < value.Count; i++)
{
lst1.Items.Add(value[i]);
lst1.Items.Add("\n");
}
DestroyPingers();
string a = "Finished in " + watch.Elapsed.ToString() + ". Found " + result + " active IP-addresses.";
lst1.Items.Add(a);
}
public static void CreatePingers(int kt)
{
for (int start = 1; start <= kt; start++)
{
// class System.Net.NetworkInformation.Ping
Ping p = new Ping();
p.PingCompleted += Ping_completed();
pingers.Add(p);
}
}
public static PingCompletedEventHandler Ping_completed()
{
PingCompletedEventHandler a = new PingCompletedEventHandler(abc);
return a;
}
static void abc(object s, PingCompletedEventArgs e)
{
value.Add("abc");
lock (#lock)
{
instances -= 1;
}
if (e.Reply.Status == IPStatus.Success)
{
string abcd = string.Concat("Active IP: ", e.Reply.Address.ToString());
value.Add(abcd);
result += 1;
}
}
public static void DestroyPingers()
{
foreach (Ping p in pingers)
{
p.PingCompleted -= Ping_completed();
p.Dispose();
}
pingers.Clear();
}
}
What is wrong in this code?
Method SendAsync returns 0 because you are not waiting for it to complete. You are missing await and async (see msdn):
async void btnscan_Click(object sender, EventArgs e)
{
...
await p.SendAsync(string.Concat(baseIP, cnt.ToString()), timeout, data,
...
}
SpinWait was making code to work in console application. In winforms you should not use SpinWait (nor Sleep) in UI thread. You can create another thread (e.g. by using Task) and then you can copy/paste code from console application 1-to-1. But then you will need to use Invoke each time when you want to access UI controls.
async/await is really better.. if it will work (I concluded that from method name, I've no idea what method does, nor how to use it).
Perhaps I miss one thing, if SendAsync returns value, then you can get it by (the requirement to mark method where you use await with async still):
var result = await p.SendAsync(...);
I am writing a WinForm application to use SNMP calls either every 30 seconds or 1 minute.
I have a timer working for calling my SNMP commands, but I want to add a texbox counter that display the total time elapsed during the operation.
There are many problems I am having so here is a list:
I want my SNMP timer 'timer' to execute once before waiting the allotted time so I have it going off at 3 seconds and then changing the interval in my handler. But this sometimes makes the timer go off multiple times which is not what I want.
Every time 'timer' goes off and my SNMP calls execute my counter timer 'appTimer' becomes out of sync. I tried a work around where I check if it is in the other handler and then just jump the timer to its appropriate time. Which works but I feel this is making it too complicated.
My last issue, that I know of, happens when I stop my application using my stop button which does not completely exit the app. When I go to start another run the time between both timers is becomes even greater and for some reason my counting timer 'appTimer' starts counting twice as fast.
I hope this description isn't too confusing but here is my code anyway:
using System;
using System.Net;
using SnmpSharpNet;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public static bool stop = false;
static bool min = true, eye = false, firstTick = false;
static string ipAdd = "", fileSaveLocation = "";
static System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
static System.Windows.Forms.Timer appTimer = new System.Windows.Forms.Timer();
static int alarmCounter = 1, hours = 0, minutes = 0, seconds = 0, tenthseconds = 0, count = 0;
static bool inSNMP = false;
static TextBox textbox, timeTextbox;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textbox = outputBox;
timeTextbox = timeBox;
ipAdd = "192.168.98.107";
fileSaveLocation = "c:/Users/bshellnut/Desktop/Eye.txt";
min = true;
inSNMP = false;
}
private void IPtext_TextChanged(object sender, EventArgs e)
{
ipAdd = IPtext.Text;
}
private void stopButton_Click(object sender, EventArgs e)
{
stop = true;
timer.Stop();
appTimer.Stop();
count = 0;
hours = minutes = seconds = tenthseconds = 0;
inSNMP = false;
}
// This is the method to run when the timer is raised.
private static void TimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
inSNMP = true;
timer.Stop();
if (firstTick == true)
{
// Sets the timer interval to 60 seconds or 1 second.
if (min == true)
{
timer.Interval = 1000 * 60;
}
else
{
timer.Interval = 1000 * 30;
}
}
// Displays a message box asking whether to continue running the timer.
if (stop == false)
{
textbox.Clear();
// Restarts the timer and increments the counter.
alarmCounter += 1;
timer.Enabled = true;
System.IO.StreamWriter file;
//if (eye == true)
//{
file = new System.IO.StreamWriter(fileSaveLocation, true);
/*}
else
{
file = new System.IO.StreamWriter(fileSaveLocation, true);
}*/
// SNMP community name
OctetString community = new OctetString("public");
// Define agent parameters class
AgentParameters param = new AgentParameters(community);
// Set SNMP version to 2 (GET-BULK only works with SNMP ver 2 and 3)
param.Version = SnmpVersion.Ver2;
// Construct the agent address object
// IpAddress class is easy to use here because
// it will try to resolve constructor parameter if it doesn't
// parse to an IP address
IpAddress agent = new IpAddress(ipAdd);
// Construct target
UdpTarget target = new UdpTarget((IPAddress)agent, 161, 2000, 1);
// Define Oid that is the root of the MIB
// tree you wish to retrieve
Oid rootOid;
if (eye == true)
{
rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.14"); // ifDescr
}
else
{
rootOid = new Oid("1.3.6.1.4.1.128.5.2.10.15");
}
// This Oid represents last Oid returned by
// the SNMP agent
Oid lastOid = (Oid)rootOid.Clone();
// Pdu class used for all requests
Pdu pdu = new Pdu(PduType.GetBulk);
// In this example, set NonRepeaters value to 0
pdu.NonRepeaters = 0;
// MaxRepetitions tells the agent how many Oid/Value pairs to return
// in the response.
pdu.MaxRepetitions = 5;
// Loop through results
while (lastOid != null)
{
// When Pdu class is first constructed, RequestId is set to 0
// and during encoding id will be set to the random value
// for subsequent requests, id will be set to a value that
// needs to be incremented to have unique request ids for each
// packet
if (pdu.RequestId != 0)
{
pdu.RequestId += 1;
}
// Clear Oids from the Pdu class.
pdu.VbList.Clear();
// Initialize request PDU with the last retrieved Oid
pdu.VbList.Add(lastOid);
// Make SNMP request
SnmpV2Packet result;
try
{
result = (SnmpV2Packet)target.Request(pdu, param);
}
catch (SnmpSharpNet.SnmpException)
{
timer.Stop();
textbox.Text = "Could not connect to the IP Provided.";
break;
}
// You should catch exceptions in the Request if using in real application.
// If result is null then agent didn't reply or we couldn't parse the reply.
if (result != null)
{
// ErrorStatus other then 0 is an error returned by
// the Agent - see SnmpConstants for error definitions
if (result.Pdu.ErrorStatus != 0)
{
// agent reported an error with the request
textbox.Text = "Error in SNMP reply. " + "Error " + result.Pdu.ErrorStatus + " index " + result.Pdu.ErrorIndex;
lastOid = null;
break;
}
else
{
// Walk through returned variable bindings
foreach (Vb v in result.Pdu.VbList)
{
// Check that retrieved Oid is "child" of the root OID
if (rootOid.IsRootOf(v.Oid))
{
count++;
textbox.Text += "#" + count + " " + v.Oid.ToString() + " " + SnmpConstants.GetTypeName(v.Value.Type) +
" " + v.Value.ToString() + Environment.NewLine;
file.WriteLine("#" + count + ", " + v.Oid.ToString() + ", " + SnmpConstants.GetTypeName(v.Value.Type) +
", " + v.Value.ToString(), true);
if (v.Value.Type == SnmpConstants.SMI_ENDOFMIBVIEW)
lastOid = null;
else
lastOid = v.Oid;
}
else
{
// we have reached the end of the requested
// MIB tree. Set lastOid to null and exit loop
lastOid = null;
}
}
}
}
else
{
//Console.WriteLine("No response received from SNMP agent.");
textbox.Text = "No response received from SNMP agent.";
//outputBox.Text = "No response received from SNMP agent.";
}
}
target.Close();
file.Close();
}
else
{
// Stops the timer.
//exitFlag = true;
count = 0;
}
}
private static void ApplicationTimerEventProcessor(Object myObject,
EventArgs myEventArgs)
{
tenthseconds += 1;
if (tenthseconds == 10)
{
seconds += 1;
tenthseconds = 0;
}
if (inSNMP && !firstTick)
{
if (min)
{
seconds = 60;
}
else
{
textbox.Text += "IN 30 SECONDS!!!";
if (seconds < 30)
{
seconds = 30;
}
else
{
seconds = 60;
}
}
}
if(seconds == 60)
{
seconds = 0;
minutes += 1;
}
if(minutes == 60)
{
minutes = 0;
hours += 1;
}
timeTextbox.Text = (hours < 10 ? "00" + hours.ToString() : hours.ToString()) + ":" +
(minutes < 10 ? "0" + minutes.ToString() : minutes.ToString()) + ":" +
(seconds < 10 ? "0" + seconds.ToString() : seconds.ToString()) + "." +
(tenthseconds < 10 ? "0" + tenthseconds.ToString() : tenthseconds.ToString());
inSNMP = false;
firstTick = false;
}
private void eyeButton_Click(object sender, EventArgs e)
{
outputBox.Text = "Connecting...";
eye = true;
stop = false;
count = 0;
hours = minutes = seconds = tenthseconds = 0;
timer.Tick += new EventHandler(TimerEventProcessor);
timer.Interval = 3000;
firstTick = true;
appTimer.Tick += new EventHandler(ApplicationTimerEventProcessor);
appTimer.Interval = 100;
appTimer.Start();
timer.Start();
}
private void jitterButton_Click(object sender, EventArgs e)
{
outputBox.Text = "Connecting...";
eye = false;
stop = false;
count = 0;
hours = minutes = seconds = tenthseconds = 0;
timer.Tick += new EventHandler(TimerEventProcessor);
timer.Interval = 3000;
firstTick = true;
appTimer.Tick += new EventHandler(ApplicationTimerEventProcessor);
appTimer.Interval = 100;
appTimer.Start();
timer.Start();
}
private void Seconds_CheckedChanged(object sender, EventArgs e)
{
min = false;
}
private void Minutes_CheckedChanged(object sender, EventArgs e)
{
min = true;
}
private void exitButton_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void savetextBox_TextChanged(object sender, EventArgs e)
{
fileSaveLocation = savetextBox.Text;
}
}
}
This is very easy to do with a single timer. The timer has a 1/10th second resolution (or so) and can be used directly to update the elapsed time. You can then use relative elapsed time within that timer to fire off your SNMP transaction, and you can reschedule the next one dynamically.
Here's a simple example
using System;
using System.Drawing;
using System.Windows.Forms;
class Form1 : Form
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
DateTime lastSnmpTime;
TimeSpan snmpTime = TimeSpan.FromSeconds(30);
DateTime startTime;
TextBox elapsedTimeTextBox;
Timer timer;
public Form1()
{
timer = new Timer { Enabled = false, Interval = 10 };
timer.Tick += new EventHandler(timer_Tick);
elapsedTimeTextBox = new TextBox { Location = new Point(10, 10), ReadOnly = true };
Controls.Add(elapsedTimeTextBox);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
startTime = DateTime.Now;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
// Update elapsed time
elapsedTimeTextBox.Text = (DateTime.Now - startTime).ToString("g");
// Send SNMP
if (DateTime.Now - lastSnmpTime >= snmpTime)
{
lastSnmpTime = DateTime.Now;
// Do SNMP
// Adjust snmpTime as needed
}
}
}
Updated Q&A
With this code the timer fires once at the beginning where after I
press the stop button and call timer.Stop() and then press my start
button the timer doesn't fire until roughly 12 seconds later. Will
resetting the DateTimes fix this?
When the user presses the Start button, set lastSnmpTime = DateTime.MinValue. This causes the TimeSpan of (DateTime.Now - lastSnmpTime) to be over 2,000 years, so it will be greater than snmpTime and will fire immediately.
Also my output time in the text box looks like this: 0:00:02.620262.
Why is that? Is there a way to make it display only 0:00:02.62?
When you subtract two DateTime values, the result is a TimeSpan value. I used a standard TimeSpan formatting string of "g". You can use a custom TimeSpan formatting string of #"d\:hh\:mm\:ss\.ff" to get days:hours:minutes:seconds.fraction (2 decimal places).
Also will the timer go on and print out to the text box when it is run
for over 9 hours? Because I plan to have this running for 24 hrs+
If you use the custom format with 'd' to show the number of days, it will run for TimeSpan.MaxValue which is slightly more than 10,675,199 days, which is more than 29,000 years.
I am trying to learn Threading in .Net.
Many of you must have seen this:
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(loop));
t.Start();
}
private void loop()
{
for (int i = 0; i < 100000; i++)
{
textBox1.Text = i.ToString();
}
}
It works fine, but what if my loop method has parameters in it, like:
private void loop(string str)
{
for (int i = 0; i < 100000; i++)
{
textBox1.Text = i + str;
}
}
Then how to call this method in my ThreadStart as ThreadStart accepts just the method name. Then how to call loop method in a different thread?
Thread t = new Thread(new ParameterizedThreadStart(loop));
t.Start("Hello world");
private void loop(object obj)
{
string str = (string)obj;
for (int i = 0; i < 100000; i++)
{
// Don't do this: you can't change a control from another thread. Danger Will Robinson!
textBox1.Text = i + str;
}
}
Note that the loop method must accept an object parameter, so you'll have to upcast the object to your type. If you don't want, you can use a closure and an anonymous method:
string str = "Hello world";
Thread t = new Thread(() => {
for (int i = 0; i < 100000; i++)
{
// Don't do this: you can't change a control from another thread. Danger Will Robinson!
textBox1.Text = i + str;
}
});
t.Start();
In this way the anonymous method will "close" around str and it will be similar as if you had passed the parameter. Similar because there are differences/problems on closing variables. In reality I would write something similar to:
string str = "Hello world";
{
string str2 = str;
Thread t = new Thread(() => {
for (int i = 0; i < 100000; i++)
{
// Don't do this: you can't change a control from another thread. Danger Will Robinson!
textBox1.Text = i + str2;
}
});
t.Start();
}
so that no one else can "touch" str2.
If you need I can find some answer on SO that explain this "problem"
You'd use ParameterizedThreadStart instead: http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx
Thread t = new Thread(new ParameterizedThreadStart(loop));
t.Start("Foo");
// Note the use of Object here to match the delegate signature
private void loop(Object state)
{
var str = state as String;
for (int i = 0; i < 100000; i++)
{
// For what it is worth, this is illegal:
// textBox1.Text = i + str;
// You need to Invoke back to the UI thread to access a control's properties:
textBox1.Invoke(()=> { textBox1.Text = i + str; });
}
}
There is a ParameterizedThreadStart class that Delegates with a single parameter can be cast to when instantiating a Thread:
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(loop));
t.Start(str);
}
private void loop(string str)
{
for (int i = 0; i < 100000; i++)
{
//the code you had is a no-no when you are multithreading;
//all UI updates must occur on the main thread
//textBox1.Text = i + str;
UpdateTextBoxText(textBox1, i+str);
}
}
private void UpdateTextBoxText(TextBox textBox, string text)
{
//the method will invoke itself on the main thread if it isn't already running there
if(InvokeRequired)
{
this.Invoke((MethodInvoker)(()=>UpdateTextBoxText(TextBox textBox, string text)));
return;
}
textBox.Text = text;
}
If you don't need very fine-grained control over when the thread starts and stops, you can leave it to the ThreadPool and use Delegate.BeginInvoke:
private void button1_Click(object sender, EventArgs e)
{
Action<string> method = loop;
method.BeginInvoke(str, null, null);
}
private void loop(string str)
{
for (int i = 0; i < 100000; i++)
{
//textBox1.Text = i + str;
UpdateTextBoxText(textBox1, i+str);
}
}
private void UpdateTextBoxText(TextBox textBox, string text)
{
//the method will invoke itself on the main thread if it isn't already running there
if(InvokeRequired)
{
this.Invoke((MethodInvoker)(()=>UpdateTextBoxText(textBox, text)));
return;
}
textBox.Text = text;
}
Like this:
new Thread(() => loop("MyString")).Start();
You don't even have to mess with ThreadStart/ParameterizedThreadStart.
Take a look at ParameterizedThreadStart, it allows you to pass parameters into your thread start function.