AlwaysStudent

Hi all,

I've defined a UserControl, that does a very long process and finally it fill its components (i.e. labels and textboxes) with the result of the process.

The main application that uses this UC, has a main form, with an instance of this UC plus a run button on it. When user presses the run button, that UC starts its processing.
What I need to do is:
I want to show a new form when user presses run button on the main form, while that UC is doing its long process, this new form shows a "please wait" message also this new form has a cancel button on it, where by pressing that cancel button, the processing would stop.
I tried your multithreading method, but the new form will not appear on the screen until the whole process of that UC on the main form is finished, therefore even if you see a ghost of that new form with Cancel button, still you cannot press its cancel button, because the other thread (for that UC) is runing in the backgraound, so application is locked.
I'm very new to multithreading, and can anyone please help me on this A sample code appreciated.



Re: Visual C# General How to run two threads concurrently while multithreading?

Dronus

Hello! it's very simple.

here example especially for you Smile

In Main Form

Code Block

public bool StopFlag=false;

public void SomeFunction()

{

while (!this.StopFlag)

{

// do something...

}

// indicate that Thread was stopped

MessageBox.Show("Stopped");

}

private void Form1_Load(object sender, System.EventArgs e)

{

// I used this in this procedure. But you can use it where you want.

// create new thread

Thread thrd=new Thread(new System.Threading.ThreadStart(this.SomeFunction));

// start new thread

thrd.Start();

// create new popupform (Form2)

Form2 frm=new Form2(this); // <-- look in popup form constructor. it's changed.

// show this form

frm.ShowDialog(this);

}

in PopUpForm

Code Block

private Form1 pForm;

public Form2(object ParentForm) // <-- !!! CHANGE THE CONCTRUCTOR

{

pForm=(Form1)ParentForm;

//

// Required for Windows Form Designer support

//

InitializeComponent();

//

// TODO: Add any constructor code after InitializeComponent call

//

}

...

private void button1_Click(object sender, System.EventArgs e)

{

// here we setting up flag in Form1 (MainForm) in true

pForm.StopFlag=true;

// and close self

this.Close();

}

hope this will be helpful for you.

P.S. sorry for my english. I'm from Russia.






Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Dear Dronus,

Thanks for your prompt reply. I tried your codes, with some little changes. Because "SomeFunction()" deals with a control wich cannot be accessed from a thread other than the thread it was created on (Cross-thread exception). Therefore I used delegates to deal with that control through SomeFunction.

But still the main issue is there, I mean: although "Thread thrd" is being started before calling the popup form (i.e. Form2), but you cannot see popup-form until the whole process of that control in the main form (i.e. Form1) is finished.

To my knowledge this is a contradiction with the concepts of multithreading, bacause we'd like to have both threads run together. Do I miss something Any suggestion I can email you my little program if you'd like.

Thanks






Re: Visual C# General How to run two threads concurrently while multithreading?

sirjis

You should consider using a BackgroundWorker for this task -- it hides some of the threading details needed to keep the UI running while a task is being performed in another thread. But it should still work doing it manually -- I'm not sure what's wrong with yours now.




Re: Visual C# General How to run two threads concurrently while multithreading?

Ji Cheng Wang - MSFT

Hi AlwaysStudent,

Just as sirjis suggested, it seems that a BackgroundWorker is suitable to your situation.You can try to check out this MSDN document about "BackgroundWorker Component" for reference - http://msdn2.microsoft.com/en-us/library/8xs8549b(vs.80).aspx

Besides, I recommend you to check out this blog about "Using BackgroundWorker Effectively" for reference - http://blogs.msdn.com/jfoscoding/archive/2005/10/30/487043.aspx

If you get any issues when using a BackgroundWorker component, please let me know. Thanks.

Hope this helps,






Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Hi Ji Cheng Wang - MSFT,

I tried backgroundworker, but still I cannot run two threads concurrently. I sent my project to your email address (
v DDASH jicwan ATTTT_SIGNNN online DDOT microsoft DOTT com). Would you please let me know what's wrong in there

Thanks mate,







Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Dear All,

Woiuld you please tell me what is wrong in my codes that causes two threads not running concurrently

Code Block for the main form:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Threading;

namespace MultiThreadingHeadache

{

public class Form1 : Form

{

private System.ComponentModel.IContainer components = null;

private System.Windows.Forms.ListBox listBox1;

private System.Windows.Forms.Button usingBGW;

private System.Windows.Forms.Button usingThrd;

private System.ComponentModel.BackgroundWorker backgroundWorker1;

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

private void InitializeComponent()

{

this.listBox1 = new System.Windows.Forms.ListBox();

this.usingBGW = new System.Windows.Forms.Button();

this.usingThrd = new System.Windows.Forms.Button();

this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();

this.SuspendLayout();

// listBox1

this.listBox1.FormattingEnabled = true;

this.listBox1.Location = new System.Drawing.Point(24, 12);

this.listBox1.Name = "listBox1";

this.listBox1.Size = new System.Drawing.Size(256, 134);

this.listBox1.TabIndex = 0;

// usingBGW

this.usingBGW.Location = new System.Drawing.Point(24, 168);

this.usingBGW.Name = "usingBGW";

this.usingBGW.Size = new System.Drawing.Size(195, 23);

this.usingBGW.TabIndex = 1;

this.usingBGW.Text = "Using \'BackgroundWorker\'";

this.usingBGW.UseVisualStyleBackColor = true;

this.usingBGW.Click += new System.EventHandler(this.usingBGW_Click);

// usingThrd

this.usingThrd.Location = new System.Drawing.Point(24, 216);

this.usingThrd.Name = "usingThrd";

this.usingThrd.Size = new System.Drawing.Size(195, 23);

this.usingThrd.TabIndex = 2;

this.usingThrd.Text = "Using \'Thread\'";

this.usingThrd.UseVisualStyleBackColor = true;

this.usingThrd.Click += new System.EventHandler(this.usingThrd_Click);

// backgroundWorker1

this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);

this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

// Form1

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.Add(this.usingThrd);

this.Controls.Add(this.usingBGW);

this.Controls.Add(this.listBox1);

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

public Form1()

{

InitializeComponent();

}

delegate void SetWinExpCallback(string path);

private void SetWinExp(string DummyValue)

{

if (this.listBox1.InvokeRequired)

{

SetWinExpCallback d = new SetWinExpCallback(SetWinExp);

this.Invoke(d, new string[] { DummyValue });

}

else

{

for (int i = 0; i < 10000; i++)

{

this.listBox1.Items.Add(DummyValue + " " + i.ToString() + ',');

//Thread.Sleep(50);

}

}

}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

SetWinExp("backgroundWorker invokes thedelegate");

}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

MessageBox.Show(">>>>>>>>> background work finished.");

if (e.Error != null)

MessageBox.Show("Failed to do Multithreading.", "Cannot do Multithreading ");

}

private void usingBGW_Click(object sender, EventArgs e)

{

FrmPleaseWait frm= new FrmPleaseWait();

frm.Show();

this.backgroundWorker1.RunWorkerAsync();

}

private void usingThrd_Click(object sender, EventArgs e)

{

FrmPleaseWait frm = new FrmPleaseWait();

frm.Show();

Thread thrd = new Thread(new System.Threading.ThreadStart(this.LongProcessFunction));

thrd.Start();

}

public void LongProcessFunction()

{

SetWinExp("'Thread' invokes the delegate");

}

}

}

And this is for the second form:

Code Block

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

namespace MultiThreadingHeadache

{

public partial class FrmPleaseWait : Form

{

private System.ComponentModel.IContainer components = null;

private System.Windows.Forms.Label label1;

private System.Windows.Forms.Button button1;

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

private void InitializeComponent()

{

this.label1 = new System.Windows.Forms.Label();

this.button1 = new System.Windows.Forms.Button();

this.SuspendLayout();

// label1

this.label1.AutoSize = true;

this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 27.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));

this.label1.ForeColor = System.Drawing.Color.Red;

this.label1.Location = new System.Drawing.Point(30, 37);

this.label1.Name = "label1";

this.label1.Size = new System.Drawing.Size(124, 42);

this.label1.TabIndex = 0;

this.label1.Text = "label1";

// button1

this.button1.Location = new System.Drawing.Point(77, 191);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(133, 23);

this.button1.TabIndex = 1;

this.button1.Text = "stop long process";

this.button1.UseVisualStyleBackColor = true;

this.button1.Click += new System.EventHandler(this.button1_Click);

// FrmPleaseWait

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.Add(this.button1);

this.Controls.Add(this.label1);

this.Name = "FrmPleaseWait";

this.Text = "FrmPleaseWait";

this.ResumeLayout(false);

this.PerformLayout();

}

int currImgNo = 0;

public FrmPleaseWait()

{

InitializeComponent();

InitializeComponent();

Timer timerWait = new Timer();

timerWait.Interval = 100;

timerWait.Tick += new EventHandler(timerWait_Tick);

timerWait.Start();

}

void timerWait_Tick(object sender, EventArgs e)

{

switch (currImgNo)

{

case 0:

case 10: label1.Text = "CASE 0,10"; currImgNo = 0; break;

case 1: label1.Text = "CASE 1"; break;

case 2: label1.Text = "CASE 2"; break;

case 3: label1.Text = "CASE 3"; break;

case 4: label1.Text = "CASE 4"; break;

case 5: label1.Text = "CASE 5"; break;

case 6: label1.Text = "CASE 6"; break;

case 7: label1.Text = "CASE 7"; break;

case 8: label1.Text = "CASE 8"; break;

case 9: label1.Text = "CASE 9"; break;

default: break;

}

currImgNo++;

}

private void button1_Click(object sender, EventArgs e)

{

}

}

}

Ji Cheng Wang - MSFT,

My email to you didn't go through!






Re: Visual C# General How to run two threads concurrently while multithreading?

sirjis

FrmPleaseWait frm= new FrmPleaseWait();

frm.Show();

this.backgroundWorker1.RunWorkerAsync();


Once this block of code exits, the frm variable goes out of scope, which means that it could be garbage collected. I'm not sure if that's what is actually happening, but you should consider making FrmPleaseWait a member variable of the class instead of a local variable and see if it helps.


Another curious thing I noticed was that you are calling InitializeComponent() twice in the second form.


If this doesn't help, can you please provide more information about what exactly happens Try putting statements like Console.WriteLine("Entering LongProcessFunction()"); in all of your functions and look at the output tab to trace what is going on. (Don't try using MessageBoxes for this).






Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Dear sirjis,

Please find below the amended codes. I tried to implement the changes that you told me. Only I'm not sure about this part: "you should consider making FrmPleaseWait a member variable of the class instead of a local variable". (can you explain how to do that )

I'm afriad but still I cannot get two threads run concurrently! Appreciate any help

Code Block for the main form:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Threading;

namespace MultiThreadingHeadache

{

public class Form1 : Form

{

private System.ComponentModel.IContainer components = null;

private System.Windows.Forms.ListBox listBox1;

private System.Windows.Forms.Button usingBGW;

private System.Windows.Forms.Button usingThrd;

private System.ComponentModel.BackgroundWorker backgroundWorker1;

private FrmPleaseWait frm;

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

private void InitializeComponent()

{

this.listBox1 = new System.Windows.Forms.ListBox();

this.usingBGW = new System.Windows.Forms.Button();

this.usingThrd = new System.Windows.Forms.Button();

this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();

this.SuspendLayout();

// listBox1

this.listBox1.FormattingEnabled = true;

this.listBox1.Location = new System.Drawing.Point(24, 12);

this.listBox1.Name = "listBox1";

this.listBox1.Size = new System.Drawing.Size(256, 134);

this.listBox1.TabIndex = 0;

// usingBGW

this.usingBGW.Location = new System.Drawing.Point(24, 168);

this.usingBGW.Name = "usingBGW";

this.usingBGW.Size = new System.Drawing.Size(195, 23);

this.usingBGW.TabIndex = 1;

this.usingBGW.Text = "Using \'BackgroundWorker\'";

this.usingBGW.UseVisualStyleBackColor = true;

this.usingBGW.Click += new System.EventHandler(this.usingBGW_Click);

// usingThrd

this.usingThrd.Location = new System.Drawing.Point(24, 216);

this.usingThrd.Name = "usingThrd";

this.usingThrd.Size = new System.Drawing.Size(195, 23);

this.usingThrd.TabIndex = 2;

this.usingThrd.Text = "Using \'Thread\'";

this.usingThrd.UseVisualStyleBackColor = true;

this.usingThrd.Click += new System.EventHandler(this.usingThrd_Click);

// backgroundWorker1

this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);

this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);

// Form1

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

this.ClientSize = new System.Drawing.Size(292, 266);

this.Controls.Add(this.usingThrd);

this.Controls.Add(this.usingBGW);

this.Controls.Add(this.listBox1);

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

public Form1()

{

InitializeComponent();

}

delegate void SetWinExpCallback(string path);

private void SetWinExp(string DummyValue)

{

if (this.listBox1.InvokeRequired)

{

SetWinExpCallback d = new SetWinExpCallback(SetWinExp);

this.Invoke(d, new string[] { DummyValue });

}

else

{

for (int i = 0; i < 100; i++)

{

Console.WriteLine("Entering LongProcessFunction-> " + DummyValue + " " + i.ToString() + ',');

this.listBox1.Items.Add(DummyValue + " " + i.ToString() + ',');

//Thread.Sleep(50);

}

}

}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

{

SetWinExp("backgroundWorker invokes thedelegate");

}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

{

Console.WriteLine(">>>>>>>>> background work finished.");

if (e.Error != null)

Console.WriteLine("Failed to do Multithreading.", "Cannot do Multithreading ");

}

private void usingBGW_Click(object sender, EventArgs e)

{

frm= new FrmPleaseWait(this);

frm.Show();

this.backgroundWorker1.RunWorkerAsync();

}

private void usingThrd_Click(object sender, EventArgs e)

{

frm = new FrmPleaseWait(this);

frm.Show();

Thread thrd = new Thread(new System.Threading.ThreadStart(this.LongProcessFunction));

thrd.Start();

}

public void LongProcessFunction()

{

SetWinExp("'Thread' invokes the delegate");

}

}

static class Program

{

[STAThread]

static void Main()

{

Application.EnableVisualStyles();

Application.SetCompatibleTextRenderingDefault(false);

Application.Run(new Form1());

}

}

}

Code Block for the PleaseWait form:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

namespace MultiThreadingHeadache

{

public partial class FrmPleaseWait : Form

{

private System.ComponentModel.IContainer components = null;

private Form1 parntFrm;

protected override void Dispose(bool disposing)

{

if (disposing && (components != null))

{

components.Dispose();

}

base.Dispose(disposing);

}

private void InitializeComponent()

{

this.SuspendLayout();

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

this.ClientSize = new System.Drawing.Size(292, 266);

this.Name = "FrmPleaseWait";

this.Text = "FrmPleaseWait";

this.ResumeLayout(false);

this.PerformLayout();

}

int currImgNo = 0;

public FrmPleaseWait()

{

InitializeComponent();

Timer timerWait = new Timer();

timerWait.Interval = 100;

timerWait.Tick += new EventHandler(timerWait_Tick);

timerWait.Start();

}

public FrmPleaseWait(object pFrm)

{

parntFrm = (Form1)pFrm;

InitializeComponent();

Timer timerWait = new Timer();

timerWait.Interval = 100;

timerWait.Tick += new EventHandler(timerWait_Tick);

timerWait.Start();

}

void timerWait_Tick(object sender, EventArgs e)

{

switch (currImgNo)

{

case 0:

case 10: Console.WriteLine("Entering please wait -> CASE 0,10"); currImgNo = 0; break;

case 1: Console.WriteLine("Entering please wait -> CASE 1"); break;

case 2: Console.WriteLine("Entering please wait -> CASE 2"); break;

case 3: Console.WriteLine("Entering please wait -> CASE 3"); break;

case 4: Console.WriteLine("Entering please wait -> CASE 4"); break;

case 5: Console.WriteLine("Entering please wait -> CASE 5"); break;

case 6: Console.WriteLine("Entering please wait -> CASE 6"); break;

case 7: Console.WriteLine("Entering please wait -> CASE 7"); break;

case 8: Console.WriteLine("Entering please wait -> CASE 8"); break;

case 9: Console.WriteLine("Entering please wait -> CASE 9"); break;

default: break;

}

currImgNo++;

}

}

}






Re: Visual C# General How to run two threads concurrently while multithreading?

Ji Cheng Wang - MSFT

AlwaysStudent wrote:

Hi Ji Cheng Wang - MSFT,

I tried backgroundworker, but still I cannot run two threads concurrently. I sent my project to your email address (
v DDASH jicwan ATTTT_SIGNNN online DDOT microsoft DOTT com). Would you please let me know what's wrong in there

Thanks mate,


Hi AlwaysStudent,

Sorry for not giving you an immediate reply. If you would like to send your project to my email box, try to remove "online." and that should be OK. I will help you take a look at your project. However, I suggest you to send part source codes with some comments to MSDN forums and you will get more helps from the C# community members in MSDN forums.

Comments:

You'd better send the sample codes with some comments to them. For example, what would you like to do Do you get what's the error message when trying to do in this way

Regards,






Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Hi Ji Cheng Wang - MSFT,

If you please look at the my last post, you'll find my codes. There is no need to send the project to your email box, you can get it from there.

In this small project, I want to fill a listbox on a mainform (suppose this is a very time consuming task), meanwhile I would like to show another form to ask user to wait.

To run the time-consuming task (filling-in the listbox) in the mainform I used two approaches, either by "Thread" class or by "BackgraoundWorker" (as you and other friends advised me). That's what two buttons do on the main form.

Well, I strat the time-consuming thread, and also want to show that pleasewaitform concurrently. But the pleasewaitform is not coming up and Console.WriteLine() commands in the pleasewaitform will NOT start displaying messages until that time-consuming thread finishes its job. That's a contradiction.

Appreciate any comment on this.






Re: Visual C# General How to run two threads concurrently while multithreading?

sirjis

I only have VS2003, so I unfortunately can't run your code to debug it. However, another possible thing that's happening is this:

The Timer component on the waiting form runs on the window thread -- the same thread that the long process is running on. This is because all Forms in an application share the same message loop thread. What happens if you add Thread.Sleep(1) calls in the long process After trying that, also see what happens if you add Application.DoEvents() inside the loop. Let us know what the result is in both cases.

You could also try using a multithreaded timer on the waiting form. Like System.Threading.Timer instead of System.Windows.Forms.Timer. This should then run the Tick events on a thread pool thread. However, you will need to then use Invoke() to marshal any calls from that Tick event that change the form (like if you switch the Console.WriteLine to change a label's text).

You could also, instead of using a timer on the waiting form, make the long process report its progress percentage through the list. Then you could update the waiting form at specific times without crossing threads, and you could have a progress bar on it.




Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Hi sirjis,

- When I added Thread.Sleep(1) call in the long process, then that thread took longer time to finsh and still the other thread didn't start while that long process was running.

- When I added Application.DoEvents() inside the loop, then it allowed other thread to be run almost simultaneously. This command causes all Windows messages currently in the message queue to be processed. In this case, I don't have any control on my two threads. I have a bad feeling about this command! However having this command in the long process makes it longer to finish due to waitings for unnecessary things.

- Using a multithreaded timer on the waiting form was a good idea. Using that, I could display messages on the console while two threads were running concurrently. But there is two issues with System.Threading.Timer:

1. It's not too accurate. Here in my sample it is not required to be very accurate at all , but in the main project that I am doing, it would be critical. I mean if I want an accurate timing for time-interval of 10 milliseconds, then this timer fails to tick on-time.

2. As you said and I experienced: "This is because all Forms in an application share the same message loop thread." Therefore even by using a System.Threading.Timer, I cannot have two forms to be displayed nicely on the screen because they share the same thread and I cannot run them on two different threads.

I guess that's the final answer, I mean VS does NOT allow runnig two threads containing two different forms concurrently.

Am I right Please confirm it or propose a workaround. Thanks






Re: Visual C# General How to run two threads concurrently while multithreading?

sirjis

As far as I know, none of the built in timers are accurate to 10 ms, but I haven't actually done tests on this.

And yes, you cannot run forms on multiple threads (at least not that I've done -- you could do it with multiple processes). You should just not make the UI so busy. Instead of Invoke, try using BeginInvoke. Also, instead of updating the list view 100 times in a row, save up the items in an array, and then once they're done being added then add them all at once.

The progress form to show the progress of a background thread is extremely common -- the only strange thing about your situation is that your background thread is UI intensive.

Then again, I may be missing the cause here. Hopefully someone more knowledgeable can give you more specific advice.




Re: Visual C# General How to run two threads concurrently while multithreading?

AlwaysStudent

Thanks sirjis.

BeginInvoke() has the same effect, I mean you can run two threads concurrently but if two threads have their own forms then you'll not be able to see one or both of those forms properly on the screen.

Here as a sample I try to fill a ListBox, in the main application I'm working on it, the background thread is a signal processing module which does some very sophisticated calculations and has got a very extensive graphics to accomplish slowly slowly as the process is being done. While that signal_processing thread is running and updating its graphics on the main form, I'd like to have the user be able to interact with the other tools in the application. Well, other tools in the application have their own forms. If there is no workaround to run forms on multiple threads, then my users should have coffee and make some phone calls to their lovers while that signal processing thread is doing its job, and meanwhile they cannot do anything useful. That is a big waste of time and money.

I appreciate anyone who can offer a workaround for running forms on multiple threads.