how to create\start a thread with parameters - c#

How do I use ThreadStart to create\start a thread with method parameters.
private void GenerateData(Type Method){
ThreadStart tStart = null;
tStart = new ThreadStart(Method);
Thread t = new Thread(tStart);
t.Start();
}
private void DoWork{
//code processing here
}
// I want to call thread like this
private void rundata(){
GenerateData(Dowork);
}

It sounds like you might just want:
private void GenerateData(ThreadStart method)
{
Thread thread = new ThreadStart(method);
thread.Start();
}
You could then certainly have:
private void DoWork()
{
...
}
private void RunData()
{
GenerateData(DoWork);
}
... although given how trivial GenerateData is, I'm not sure it's really worth it... you could just have:
private void RunData()
{
new Thread(DoWork).Start();
}
instead.

Related

Can we invoke method outside the thread block by any technique in C#? so that we can use same thread method to invoke other methods as well.?

e.g.
Here in this example, if I want to start thread on method M1 this way,
Purpose is I need to call M2, M3 on same thread method to avoid repeated code.
Is this possible in C#?
static void Main()
{
if(StartThread() => M1()) //I want to invoke Method M1 from here like this
{
return;
}
}
private void StartThread()
{
var t = new Thread(() => M1()); //I dont want to invoke Method M1 from here
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
private static void M1()
{
throw new NotImplementedException();
}
Just use a delegate (e.g. an Action) and pass it to StartThread:
static void Main()
{
if(StartThread(() => M1()))
return;
}
private void StartThread(Action a)
{
var t = new Thread(() => a());
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
private static void M1()
{
throw new NotImplementedException();
}

How to wait for thread to complete without blocking UI

I want my program to wait after below line
frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
as above method is internally calling thread through StartProcessWithProgress() method . I want that thread to be completed before //code logic -2 line gets executed. At the same time, It should not stop UI update done by frmProgressBar.UpdateProgress(). How do I do this?
namespace NS1
{
public partial class frmMain : Form
{
private void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
//code logic - 2
MessageBox.Show("This is executing immediately.
I want to wait until above thread is complete");
}
}
public partial class frmProgressBar : Form
{
public void UpdateProgress(String strTextToDisplayOnProgress)
{
progressBar1.BeginInvoke(
new Action(() =>
{
progressBar1.Value++;
lblFileName.Text = strTextToDisplayOnProgress;
if (progressBar1.Value == progressBar1.Maximum)
{
this.Hide();
}
}));
}
public delegate void DelProgress();
public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount)
{
InitializeProgress(maxCount);
Thread backgroundThread = new Thread(new ThreadStart(delMethodCode));
backgroundThread.Start();
}
}
public static class PullMSI
{
public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI)
{
frmProgressBar frmProgressBar = new frmProgressBar();
frmProgressBar.StartProcessWithProgress(() =>
{
//StreamRader sr declaration and other code
while (!sr.EndOfStream)
{
//logic here
frmProgressBar.UpdateProgress("Copying sr.msiname");
}
}, 2);
return frmProgressBar;
}
}
}
I'm very surprised you haven't worked with any of these before but I would really recommend reading about threading in C# since it's fundamentally important to understand the intricacies and learning the language.
Below are three different ways you can achieve what you want:
1. Using reset events (further reading: https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with WaitOne()
class LockingWithResetEvents
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
public void Test()
{
MethodUsingResetEvents();
}
private void MethodUsingResetEvents()
{
ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
_resetEvent.Set();
}
private void ShowMessageBox()
{
_resetEvent.WaitOne();
Console.WriteLine("Hello world.");
}
}
2) Using Task Parallel Library (TPL). Further reading: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
class LockingWithTPL
{
public void Test()
{
Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
}
private void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(1000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
3) Using Async/Await. Further reading: https://msdn.microsoft.com/en-us/library/hh191443.aspx
class LockingWithAwait
{
public void Test()
{
DoSomething();
}
private async void DoSomething()
{
await Task.Run(() => DoSomethingLong());
ShowMessageBox();
}
private async void DoSomethingLong()
{
Console.WriteLine("Doing somthing.");
Thread.Sleep(10000);
}
private void ShowMessageBox()
{
Console.WriteLine("Hello world.");
}
}
Also good to know: Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx), Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx), Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx), SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx), Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) and Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx).
If you're using .NET 4.0 (with VS2012) or above, you can do this quite easily with the Task Parallel Library and async-await:
private async void button1_Click(object sender, EventArgs e)
{
frmProgressBar frmProgressBarObj = await Task.Run(() =>
PullMSI.ExtractByMSIName("products.txt", false));
MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString());
}
For .NET 4, you'll need to add Microsoft.Bcl.Async.

How can i use a BackgroundWorker with a timer tick?

Decided to not use any timers.
What i did is simpler.
Added a backgroundworker.
Added a Shown event the Shown event fire after all the constructor have been loaded.
In the Shown event im starting the backgroundworker async.
In the backgroundworker DoWork im doing:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
cpuView();
gpuView();
Thread.Sleep(1000);
}
}
In this case it's better to use two System.Threading.Timer and execute your cpu-intensive operations in these two threads. Please note that you must access controls with BeginInvoke. You can encapsulate those accesses into properties setter or even better pull them out to a view model class.
public class MyForm : Form
{
private System.Threading.Timer gpuUpdateTimer;
private System.Threading.Timer cpuUpdateTimer;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!DesignMode)
{
gpuUpdateTimer = new System.Threading.Timer(UpdateGpuView, null, 0, 1000);
cpuUpdateTimer = new System.Threading.Timer(UpdateCpuView, null, 0, 100);
}
}
private string GpuText
{
set
{
if (InvokeRequired)
{
BeginInvoke(new Action(() => gpuLabel.Text = value), null);
}
}
}
private string TemperatureLabel
{
set
{
if (InvokeRequired)
{
BeginInvoke(new Action(() => temperatureLabel.Text = value), null);
}
}
}
private void UpdateCpuView(object state)
{
// do your stuff here
//
// do not access control directly, use BeginInvoke!
TemperatureLabel = sensor.Value.ToString() + "c" // whatever
}
private void UpdateGpuView(object state)
{
// do your stuff here
//
// do not access control directly, use BeginInvoke!
GpuText = sensor.Value.ToString() + "c"; // whatever
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (cpuTimer != null)
{
cpuTimer.Dispose();
}
if (gpuTimer != null)
{
gpuTimer.Dispose();
}
}
base.Dispose(disposing);
}
You can't just throw this code into a background worker and expect it to work. Anything that updates UI elements (labels, textboxes, ...) needs to be invoked on the main thread. You need to break out your logic to get the data and the logic to update the UI.
I would say your best bet is to do this:
In the timer Tick() method:
// Disable the timer.
// Start the background worker
In the background worker DoWork() method:
// Call your functions, taking out any code that
// updates UI elements and storing this information
// somewhere you can access it once the thread is done.
In the background worker Completed() method:
// Update the UI elements based on your results from the worker thread
// Re-enable the timer.
First make sure to get your head around multithreathing and it's problems (especially UI stuff).
Then you can use somethink like
public class Program
{
public static void Main(string[] args)
{
Timer myTimer = new Timer(TimerTick, // the callback function
new object(), // some parameter to pass
0, // the time to wait before the timer starts it's first tick
1000); // the tick intervall
}
private static void TimerTick(object state)
{
// less then .NET 4.0
Thread newThread = new Thread(CallTheBackgroundFunctions);
newThread.Start();
// .NET 4.0 or higher
Task.Factory.StartNew(CallTheBackgroundFunctions);
}
private static void CallTheBackgroundFunctions()
{
cpuView();
gpuView();
}
}
Please keep in mind (just like John Koerner told you) your cpuView() and gpuView() will not work as is.
Yes you can:
In your Timer tick event:
private void timer_Tick(object sender, EventArgs e)
{
timer.Enabled = false;
backgroundworker.RunWorkerAsync();
timer.Enabled = true;
}
In your Backgroundworker dowork event:
private void backgroundworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
try
{
//Write what you want to do
}
catch (Exception ex)
{
MessageBox.Show("Error:\n\n" + ex.Message, "System", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
I think BackgroundWorker is too complex thing for the case; with Timer it is difficult to implement guaranteed stopping.
I would like to recommend you using worker Thread with the loop which waits cancellation ManualResetEvent for the interval you need:
If the cancellation event is set then the worker exits the loop.
If there is a timeout (time interval you need exceeds) then perform system monitoring.
Here is the draft version of the code. Please note I have not tested it, but it could show you the idea.
public class HardwareMonitor
{
private readonly object _locker = new object();
private readonly TimeSpan _monitoringInterval;
private readonly Thread _thread;
private readonly ManualResetEvent _stoppingEvent = new ManualResetEvent(false);
private readonly ManualResetEvent _stoppedEvent = new ManualResetEvent(false);
public HardwareMonitor(TimeSpan monitoringInterval)
{
_monitoringInterval = monitoringInterval;
_thread = new Thread(ThreadFunc)
{
IsBackground = true
};
}
public void Start()
{
lock (_locker)
{
if (!_stoppedEvent.WaitOne(0))
throw new InvalidOperationException("Already running");
_stoppingEvent.Reset();
_stoppedEvent.Reset();
_thread.Start();
}
}
public void Stop()
{
lock (_locker)
{
_stoppingEvent.Set();
}
_stoppedEvent.WaitOne();
}
private void ThreadFunc()
{
try
{
while (true)
{
// Wait for time interval or cancellation event.
if (_stoppingEvent.WaitOne(_monitoringInterval))
break;
// Monitoring...
// NOTE: update UI elements using Invoke()/BeginInvoke() if required.
}
}
finally
{
_stoppedEvent.Set();
}
}
}
In my case I was using a BackgroundWorker ,a System.Timers.Timer and a ProgressBar in WinForm Application. What I came across is on second tick that I will repeat the BackgroundWorker's Do-Work I get a Cross-Thread Exception while trying to update ProgressBar in ProgressChanged of BackgroundWorker .Then I found a solution on SO #Rudedog2 https://stackoverflow.com/a/4072298/1218551 which says that When you initialize the Timers.Timer object for use with a Windows Form, you must set the SynchronizingObject property of the timer instance to be the form.
systemTimersTimerInstance.SynchronizingObject = this; // this = form instance.
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx

Multi threading; pass object to another object

I need to pass an object to another object. I know I have to pass c to t1. How do I do this
Thread t = new Thread(t1);
t.Start();
private static void t1(Class1 c)
{
while (c.process_done == false)
{
Console.Write(".");
Thread.Sleep(1000);
}
}
Ok guys, everybody is missing the point the object is being used outside the thread as well. This way, it must be synchronized to avoid cross-thread exceptions.
So, the solution would be something like this:
//This is your MAIN thread
Thread t = new Thread(new ParameterizedThreadStart(t1));
t.Start(new Class1());
//...
lock(c)
{
c.magic_is_done = true;
}
//...
public static void t1(Class1 c)
{
//this is your SECOND thread
bool stop = false;
do
{
Console.Write(".");
Thread.Sleep(1000);
lock(c)
{
stop = c.magic_is_done;
}
while(!stop)
}
}
Hope this helps.
Regards
You could simply do:
Thread t = new Thread(new ParameterizedThreadStart(t1));
t.Start(new Class1());
public static void t1(object c)
{
Class1 class1 = (Class1)c;
...
}
MSDN: ParameterizedThreadStart Delegate
Or even better:
Thread thread = new Thread(() => t1(new Class1()));
public static void t1(Class1 c)
{
// no need to cast the object here.
...
}
This approach permits multiple arguments and does not require you to cast the object to the desired class/struct.
private static void DoSomething()
{
Class1 whatYouWant = new Class1();
Thread thread = new Thread(DoSomethingAsync);
thread.Start(whatYouWant);
}
private static void DoSomethingAsync(object parameter)
{
Class1 whatYouWant = parameter as Class1;
}

Calling a method when thread terminates

I have a form that starts a thread. Now I want the form to auto-close when this thread terminates.
The only solution I found so far is adding a timer to the form and check if thread is alive on every tick. But I want to know if there is a better way to do that?
Currently my code looks more less like this
partial class SyncForm : Form {
Thread tr;
public SyncForm()
{
InitializeComponent();
}
void SyncForm_Load(object sender, EventArgs e)
{
thread = new Thread(new ThreadStart(Synchronize));
thread.IsBackground = true;
thread.Start();
threadTimer.Start();
}
void threadTimer_Tick(object sender, EventArgs e)
{
if (!thread.IsAlive)
{
Close();
}
}
void Synchronize()
{
// code here
}
}
The BackgroundWorker class exists for this sort of thread management to save you having to roll your own; it offers a RunWorkerCompleted event which you can just listen for.
Edit to make it call a helper method so it's cleaner.
thread = new Thread(() => { Synchronize(); OnWorkComplete(); });
...
private void OnWorkComplete()
{
Close();
}
If you have a look at a BackgroundWorker, there is a RunWorkerCompleted event that is called when the worker completes.
For more info on BackgroundWorkers Click Here
Or
You could add a call to a complete function from the Thread once it has finished, and invoke it.
void Synchronize()
{
//DoWork();
//FinishedWork();
}
void FinishedWork()
{
if (InvokeRequired == true)
{
//Invoke
}
else
{
//Close
}
}
Have a look at delegates, IAsyncResult, BeginInvoke and AsyncCallback
At the end of your thread method, you can call Close() using the Invoke() method (because most WinForms methods should be called from the UI thread):
public void Synchronize()
{
Invoke(new MethodInvoker(Close));
}
Solution for arbitrary thread (e.g. started by some other code), using UnmanagedThreadUtils package:
// Use static field to make sure that delegate is alive.
private static readonly UnmanagedThread.ThreadExitCallback ThreadExitCallbackDelegate = OnThreadExit;
public static void Main()
{
var threadExitCallbackDelegatePtr = Marshal.GetFunctionPointerForDelegate(ThreadExitCallbackDelegate);
var callbackId = UnmanagedThread.SetThreadExitCallback(threadExitCallbackDelegatePtr);
for (var i = 1; i <= ThreadCount; i++)
{
var threadLocalVal = i;
var thread = new Thread(_ =>
{
Console.WriteLine($"Managed thread #{threadLocalVal} started.");
UnmanagedThread.EnableCurrentThreadExitEvent(callbackId, new IntPtr(threadLocalVal));
});
thread.Start();
}
UnmanagedThread.RemoveThreadExitCallback(callbackId);
}
private static void OnThreadExit(IntPtr data)
{
Console.WriteLine($"Unmanaged thread #{data.ToInt64()} is exiting.");
}

Categories

Resources