I have a problem using threads. There is a class like this:
public class MyThread
{
public void Thread1(int a)
{
for (int i = 0; i < 1000000; i++)
{
j++;
for (int i1 = 0; i1 < 1000; i1++)
{
j++;
}
}
MessageBox.Show("Done From Class");
}
}
and I use this below code for using it:
private void button1_Click(object sender, EventArgs e)
{
MyThread thr = new MyThread();
Thread tid1 = new Thread(new ThreadStart(thr.Thread1));
tid1.Start();
MessageBox.Show("Done");
}
I get error because of Thread1 Parameter (int a),
there isn't any problem when I haven't got any parameter.
How can I fix it?
A preferred method is the first one as you can pass multiple parameters to your method without having to cast to object all the time.
Thread t= new Thread(() => thr.Thread1(yourparameter));
t.Start();
Alternatively, you need to use parameterised thread as you are passing parameter to thread. you can also do
Thread t = new Thread (new ParameterizedThreadStart(thr.Thread1));
t.Start (yourparameter);
ofcourse your parameter has to be of object type for second example.
Threads accept a single object parameter:
public void Thread1(object a1)
{
int a = (int)a1;
...
}
Pass it like this:
Thread t = new Thread(Thread1);
t.Start(100);
You don't normally need to build delegates. Doing new ThreadStart(...) is normally useless from C# 2.0 .
Another (common) solution is to put Thread1 in another object:
public class MyThread
{
public int A;
public void Thread1()
{
// you can use this.A from here
}
}
var myt = new MyThread();
myt.A = 100;
var t = new Thread(myt.Thread1)
t.Start();
This because delegates have a reference to the containing object of the method. Clearly in this way you lose access to the caller's object... But then you could do:
public class MyThread
{
public int A;
public CallerType ParentThis;
public void Thread1()
{
// you can use this.A from here
// You can use ParentThis.Something to access the caller
}
}
var myt = new MyThread();
myt.A = 100;
myt.ParentThis = this;
var t = new Thread(myt.Thread1)
t.Start();
A final common method is to use closures, as suggested by Ehsan Ullah (the example with the () =>)
Related
I am new to multi-thread programming in C#. My problem is that I don't know how to wait for a method that is being run on another thread to finish, before it can continue to the next line. For example, something like this
public class A
{
int i;
public A()
{
i = 0;
}
protected void RunLoop()
{
while(i < 100)
{
i++;
}
}
public void Start()
{
TimerResolution.TimeBeginPeriod(1);
runThread = new Thread(new ThreadStart(RunLoop));
running = true;
runThread.Start();
}
}
public class B
{
A classAInstance = new A();
A.Start();
Console.Writeline(i);
}
Right now, it prints 0 on the console, which is not what I want (i.e. i = 100).
What is the best way to do this? BTW, I don't have access to the runThread that is created in class A
Thanks.
EDIT:
It was a bit difficult to solve this problem without modifying a lot codes. Therefore, we ended up with adding a condition in the public void Start() with which it can decide whether to run the RunLoop in a separate thread or not. The condition was defined using an Enum field.
public void Start()
{
TimerResolution.TimeBeginPeriod(1);
running = true;
if (runningMode == RunningMode.Asynchronous)
{
runThread = new Thread(new ThreadStart(RunLoop));
runThread.Start();
}
else
{
RunLoop();
}
}
And
public enum RunningMode { Asynchronous, Synchronous };
Thanks everyone for help.
The preferred method is to use the Task Parallel Library (TPL) and use Task with await.
If you must use Threads, then use a ManualResetEvent or ManualResetEventSlim to signal the end of a method.
void Main()
{
var a = new A();
a.Start();
a.FinishedEvent.WaitOne();
Console.WriteLine(a.Index);
}
// Define other methods and classes here
public class A
{
ManualResetEvent mre = new ManualResetEvent(false);
int i;
public EventWaitHandle FinishedEvent
{
get { return mre; }
}
public int Index
{
get { return i; }
}
public A()
{
i = 0;
}
protected void RunLoop()
{
while (i < 1000)
{
i++;
}
mre.Set();
}
public void Start()
{
var runThread = new Thread(new ThreadStart(RunLoop));
runThread.Start();
}
}
Your life would be so much better with tasks.
Your code could be this simple:
var task = Task.Factory.StartNew(() =>
{
var i = 0;
while (i < 100)
{
i++;
}
return i;
});
Console.WriteLine(task.Result);
I like use Monitor.Wait() and Monitor.Pulse() in conjunction with "lock" operator. It works, but you must be careful, when you use this technique.
I'm added some changes to your code to demonstrate it. Code below are prints i== 100, as you want.
public class A
{
int i;
public object SyncObject
{ get; private set; }
public A()
{
SyncObject = new object();
i = 0;
}
protected void RunLoop()
{
while (i < 100)
{
i++;
}
lock (SyncObject)
{
Monitor.Pulse(SyncObject);
}
}
public void Start()
{
var runThread = new Thread(new ThreadStart(RunLoop));
runThread.Start();
}
public void PrintI()
{
Console.WriteLine("I == " + i);
}
}
public class B
{
public static void Run()
{
A classAInstance = new A();
lock (classAInstance.SyncObject)
{
classAInstance.Start();
Monitor.Wait(classAInstance.SyncObject);
}
classAInstance.PrintI();
}
}
I can send one parameter in the thread
class myThread
{
Thread thread;
public myThread(string name, int st)
{
thread = new Thread(this.Get_IP);
thread.Name = name;
thread.Start(st);//передача параметра в поток
thread.Join();
}
void Get_IP(object st)
{
for (int ii = 0; ii < (int)st; ii++)
{
// smth
}
}
}
But i need to send two of them
for example
for (int ii = (int)st; ii < (int)fi; ii++)
{
// smth
}
there is a way to put 2 params all together
void A(int a, int b) { }
and
ThreadStart starter = delegate { A(0, 10); };
But how can i send them to the thread?
You can pass more parameters to thread by using lambda expresion. Like this:
Thread thread = new Thread(()=>A(5,6));
Put the two variables as members in the class:
class MyThread {
private Thread _thread;
private int _start, _finish;
public MyThread(string name, int start, int finish) {
_start = start;
_finish = finish;
_thread = new Thread(Get_IP);
_thread.Name = name;
_thread.Start();
_thread.Join();
}
void Get_IP() {
for (int ii = _start; ii < _finish; ii++) {
// smth
}
}
}
Note: Calling Join right after starting the thread makes is pretty pointless to use a thread.
The Thread.Start method accepts an object as parameter. You can pass an array of your values.
thread.Start(new object[] { a, b });
Create a class that hold all of the values you need to pass and pass an instance of that class to your thread.
If you need to send 2 parametes, you can send them as any type you like, but in the method that starts new thread, you have to unbox it/them:
void MyMethod()
{
int a = 1;
int b = 2;
int[] data = new[] { a, b };
Thread t = new Thread(new ParameterizedThreadStart(StartThread));
t.Start(data);
}
private void StartThread(object obj)
{
int[] data = obj as int[];
if (data != null)
{
int a = data[0];
int b = data[1];
}
}
NOTE: method that is called by new Thread can only accppt object parameter. What is inside this object is not code`s concern, can be anything, like I boxes 2 integers.
Then you simply unbox the object to your original data types.
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;
}
Sending Parameters to the Function called by a thread in C#,
this is my code:
myThread = new Thread(new ThreadStart(myfunc));
myThread.Start(i);
public void myfunc(int i)
{
...
}
I get error:
No overload for 'installDrivers' matches delegate
'system.Threading.ThredStart'
You can use a ParameterizedThreadStart.
Thread myThread = new Thread(new ParameterizedThreadStart(myfunc));
myThread.Start(i);
And your function
public void myfunc(object i)
{
int myInt = Convert.ToInt32(i);
}
Another option, utilizing lambdas makes it easy to call functions with any number of parameters, it also avoids the rather nasty conversion from object in the one parameter case:
int paramA = 1;
string paramB = "foo";
var myThread = new Thread(() => SomeFunc(paramA, paramB));
myThread.Start();
public void SomeFunc(int paramA, string paramB)
{
// Do something...
}
use this:
myThread = new Thread(new ParameterizedThreadStart(myfunc));
myThread.Start(i);
public void myfunc(object i) {... }
it might be usefull for your issue
Unless you are using C# < 2.0, you don't need to create a delegate. You can let the compiler implicitly create it for you.
myThread = new Thread(myfunc);
myThread.Start(i);
public void myfunc(object i)
{
int i2 = (int)i; // if i is an int
}
but note that the Thread constructor accepts only two types of delegates: one that is parameterless (ThreadStart) and one that accepts an object parameter (ParameterizedThreadStart). So here we are using the second one.
Can some give me hand here? I need this method to invoke the user's desired method in a new thread based on the user's choice from the main form. I think i am close, but I am stuck at how to pass in a variable the represents that user's choice of method in the new thread start.
heres my code
public void GetTest()
{
_t = testListBox.SelectedIndex;
if (_t < 0)
return;
_v = testListBox.SelectedValue;
method = _v as MethodInfo;
if (method == null)
return;
_selectedMethod = method.Name;
MessageBox.Show(_selectedMethod.ToString());
counter++;
Thread UIthread = new Thread(new ThreadStart(??????)); // this is will be a method based on the user's choice in the main form thread
// adding Name of new Thread
UIthread.Name = "UIThread";
UIthread.Start();
// Update test status
_testStatus = "Running";
//Make thread global
_UIthread = UIthread;
}
How about something like this.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LoadList();
}
private void LoadList()
{
lstTest.Items.Clear();
var t1 = new MethodInfo
{
MethodName = "Test1",
MethodToRun = this.Test1
};
lstTest.Items.Add(t1);
var t2 = new MethodInfo
{
MethodName = "Test2",
MethodToRun = this.Test2
};
lstTest.Items.Add(t2);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
var t = lstTest.SelectedItem as MethodInfo;
if (t != null)
{
var a = new Task(t.MethodToRun);
a.Start();
}
}
private void Test1()
{
MessageBox.Show("Test1 started");
}
private void Test2()
{
MessageBox.Show("Test2 started");
}
}
public class MethodInfo
{
public string MethodName;
public Action MethodToRun;
public override string ToString()
{
return MethodName;
}
}
You can execute a lamdba expression on your new thread.
I'm not sure if you are dealing with static or instance methods. If your methods are static then you don't need to create an object instance:
method = _v as MethodInfo;
object[] parameters = null;
Thread UIthread = new Thread(() =>
{
object obj = null;
if (!method.IsStatic)
{
obj = Activator.CreateInstance(method.DeclaringType);
}
method.Invoke(obj, parameters);
});
UIthread.Start();
Thanks to everyone for all the help. Here's the answer for all the folks that care. You Create a thread start delegate. I found this out after reading these two posts: http://bytes.com/topic/net/answers/410294-using-reflection-multithreading http://www.pelennorfields.com/matt/2009/03/13/createdelegate-error-binding-to-target-method/
This method takes a reflected MethodInfo GetMethod listbox value and invokes the corresponding Instance method in a new thread. The thread is made global to allow the user to control the long-running method. The method is great because it allows for a dynamic class of instance methods, with out recompiling the entire program.code here
public void GetTest()
{
_t = testListBox.SelectedIndex;
if (_t < 0)
return;
_v = testListBox.SelectedValue;
method = _v as MethodInfo;
if (method == null)
return;
_selectedMethod = method.Name;
MessageBox.Show(_selectedMethod.ToString());
counter++;
ThreadStart ts = (ThreadStart)Delegate.CreateDelegate(typeof(ThreadStart), this, method, true);
Thread UIthread = new Thread(ts);
UIthread.Start();
_UIthread = UIthread;
}