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;
}
Related
Hi I have a use case where I am supposed to use C# Web Browser inside a form and get some data from it. However as we know the C# browser runs under STA hence I have created an instance of SmartThread with single thread architecture.
On some events from an MVC app I am supposed to invoke the web browser and get the result. When the browser is instantiated at that moment I can invoke any JavaScript method, however when I wait for an event in MVC and invoke on event the SmartThread doesn't seems to invoke the webbrowser.
Here is my below code please help me.
public class FormBrowser
{
public static Form1 form1 = null;
public static SmartThreadPool smartThreadPool;
public static void initialise()
{
if (smartThreadPool == null)
{
STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.StartSuspended = true;
stpStartInfo.ApartmentState = ApartmentState.STA;
stpStartInfo.MaxWorkerThreads = 1;
smartThreadPool = new SmartThreadPool(stpStartInfo);
var ab = smartThreadPool.QueueWorkItem(new WorkItemCallback(initForm), "line");
smartThreadPool.Start();
//smartThreadPool.Shutdown();
}
}
public static object initForm(object msg)
{
var id = Thread.CurrentThread.ManagedThreadId;
form1 = new Form1();
System.Windows.Forms.Application.Run(form1);
return null;
}
public static void redraw()
{
var id = Thread.CurrentThread.ManagedThreadId;
smartThreadPool.QueueWorkItem(new WorkItemCallback(rd), "line");
}
public static object rd(object msg)
{
var id = Thread.CurrentThread.ManagedThreadId;
form1.webBrowser1.Document.InvokeScript("startDrawing");
return null;
}
}
On calling initialise() method SmartThread will create a thread pool and then creates a Form instance which holds a Web Browser. Next when any event occures from client say he request for a latest data I have to call method redraw() and which should internally call rd() but that never happens.
Please help me to fix this, if I am missing something let me know.
The below code worked for me.
public class FormBrowser
{
public static Form1 form1 = null;
public static SmartThreadPool smartThreadPool;
public static SynchronizationContext ctx;
public static string html;
public static void initialise()
{
if (smartThreadPool == null)
{
STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.StartSuspended = false;
stpStartInfo.ApartmentState = ApartmentState.STA;
stpStartInfo.MaxWorkerThreads = 1;
smartThreadPool = new SmartThreadPool(stpStartInfo);
var ab = smartThreadPool.QueueWorkItem(new WorkItemCallback(initForm), "line");
smartThreadPool.Start();
//smartThreadPool.Shutdown();
}
}
public static object initForm(object msg)
{
var id = Thread.CurrentThread.ManagedThreadId;
form1 = new Form1();
ctx = WindowsFormsSynchronizationContext.Current;
System.Windows.Forms.Application.Run(form1);
return null;
}
public static string redraw(string chartType)
{
ctx.Send(rd, chartType);
return html;
}
public static void rd(object chartType)
{
var id = Thread.CurrentThread.ManagedThreadId;
html = (string)form1.webBrowser1.Document.InvokeScript("drawChart", new object[] { chartType });
}
}
If SmartThreadPool used along with the SynchronizationContext works well. However using SynchronizationContext alone or SmartThreadPool alone won't solve the issue.
WORKER CLASS1:
public override void Process(Crawler crawler, PropertyBag propertyBag)
{
SynchronizationContext uiContext = SynchronizationContext.Current;
Thread thread = new Thread(ThreadFunc);
}
void ThreadFunc(object state)
{
var syncContext = state as SynchronizationContext;
syncContext.Send(new Action(() => {
using (GeckoBrowserForm geckoBrowserForm = new GeckoBrowserForm ("http//www.google.com"))
{
geckoBrowserForm.ShowDialog();
while (!geckoBrowserForm.Done)
{
Application.DoEvents();
}
other code
}
}), null);
}
But I Always get
"Argument 'System.Action' is not assignable to parameter type
'System.Threading.SendOrPostCallback'"
I want that every Process will have own SynchronizationContext and then execute UIS Thread with some logic.
UI THREAD CLASS2:
protected override void OnLoad(EventArgs e)
{
...
String val = "Some value";
Done = true;
and return back `val` to the `CLASS1`
}
is this possible?
From MSDN.
public virtual void Send(SendOrPostCallback d, Object state)
public delegate void SendOrPostCallback(Object state)
Clearly, the Send method takes a delegate with an object parameter and void return value. You are passing a delegate without a parameter. Try
syncContext.Send(new Action(o => { ...
Your SendOrPostCallback() lambda needs to take one parameter of object type. See the MSDN page.
Try changing your call to this (note the lambda changed to (o) from ()):
syncContext.Send(new Action((o) => {
using (GeckoBrowserForm geckoBrowserForm = new GeckoBrowserForm ("http//www.google.com"))
{
geckoBrowserForm.ShowDialog();
while (!geckoBrowserForm.Done)
{
Application.DoEvents();
}
// other code
}
}), null);
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 () =>)
When a method is executed in a thread. Does it also calls the (default/parameterized) constructor of the class containing that method before calling the method.
public class Class1
{
HttpContext h = null;
public Class1(HttpContext _h)
{
h = _h;
}
public Class1()
{
if (h != null)
{
HttpContext.Current = h;
}
}
ManualResetEvent[] wsManualResetEvents = new ManualResetEvent[1];
public void callThread()
{
HttpContext.Current.Session["ok"] = "ll";
wsManualResetEvents[0] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback(o => me()));
if (wsManualResetEvents != null && wsManualResetEvents[0] != null)
{
WaitHandle.WaitAll(wsManualResetEvents);
}
}
private void me()
{
var d = HttpContext.Current.Session["ok"].ToString();
wsManualResetEvents[0].set();
}
}
What I am trying to do, is to set the httpcontext of the thread equivalent to the executing thread.
No, starting a thread does not call the constructor. In the case of your code, you have an instance of Class1 and you're calling .callThread() on it. When me() is invoked on the background thread, it's calling me() on the same instance of Class1. Just because you've started a new thread doesn't mean you've created a new Class1 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;
}