Threaded Code Is Causing the UI To Hang - c#

I wrote a simple class to generate arrays as I needed some hard-coded random arrays for my own debugging purposes, however despite calling the timely operations in a separate worker thread my UI still hangs! Here's the code...
private static Random randGenerator = new Random();
private void generateButton_Click(object sender, EventArgs e)
{
string dataType = "System.";
if (typeComboBox.Text != "Byte")
dataType += signedCheckBox.Checked ? "" : "U";
else if (typeComboBox.Text == "Byte")
dataType += signedCheckBox.Checked ? "S" : "";
dataType += typeComboBox.Text;
generateButton.Enabled = false;
new Thread(() =>
{
Process(Type.GetType(dataType), (int)sizeNumericUpDown.Value, hexCheckBox.Checked);
}).Start();
}
private void Process(Type type, int size, bool hex)
{
StringBuilder sBuilder = new StringBuilder();
sBuilder.Append(string.Format(#"{0}[] values = new {0}[{1}] {{", type.Name, size));
for (int i = 0; i < size; i++)
{
int random = randGenerator.Next(0, GetIntegralMaxValue(type));
sBuilder.Append((hex ? "0x" + random.ToString("x2") : random.ToString()) + (i < size - 1 ? ", " : " };"));
}
outputTextBox.BeginInvoke((MethodInvoker)delegate
{
outputTextBox.Text = sBuilder.ToString();
});
}
private int GetIntegralMaxValue(Type type)
{
var field = type.GetField("MaxValue").GetValue(null);
return Convert.ToInt32(field);
}
I thought that maybe the issue lied with trying to access objects created on the main thread so instead I passed them to the method. I also tried declaring my randGenerator object within the class but still no luck. Could anybody identify the issue?

The TextBox is not designed to hold a large set of data - it becomes very slow when amount of data increases. I would say that populating of the StringBuilder takes about 5% of time while assigning this data to TextBox (which executes in UI thread) takes remaining 95% of time. You can easily check this. Just run this code in debug mode and click "Pause" button during hanging. It should break on "outputTextBox.Text = sBuilder.ToString();" line.

Related

Why Lock statement doesn't work as expected

static List<int> sharedCollection = new List<int>();
static readonly Object obj = new Object();
static void Main(string[] args)`enter code here`
{
var writeThread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
lock (obj)
{
Write();
}
}
});
var readThread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
lock (obj)
{
Read();
}
}
});
writeThread.Start();
readThread.Start();
Console.ReadLine();
}
static void Read()
{
Console.Write("Current collection state: ");
sharedCollection.ForEach((e) => Console.Write($"{e} "));
Console.WriteLine();
}
static void Write()
{
Random generator = new Random();
var addedValue = generator.Next(1, 20);
sharedCollection.Add(addedValue);
Console.WriteLine($"Added value is: {addedValue}");
}
I spend a lot of time trying to understand why I receive this:
console result
Could someone explain to me what is wrong with this code?
Mutex works fine but I need to illustrate lock statement too...
I expect that after every adding in 1st thread I obtain a collection state from the 2nd thread. Like this:
Added value: 1
Collection state: 1
Added value: 15
Collection state: 1 15
Added value: 4
Collection state: 1 15 4
I understand you expeected those threasd to run somewhat in paralell, but instead they executed sequentially. You expectation is correct.
I do not think it has anything to do with lock, however. lock will only prevent a read and a write from happening at the same time, not produce this behavior. Try it without the lock to verify. (However due to things like the JiT Compiler, CPU cache invalidations and Optimisations, results may still differet if there is a lock, even if it has no direct effect).
My best bet is that the read thread is simply so slow, it does not finish once before the write is through all it's itteartions. Writing the UI is expensive, even on something as trivial as the console. Or even especially there. I do a lot of backups of userprofiles using robocopy. And if it hits a lot of very small files, just writing the Console becomes the actuall programm bottleneck, ever over disk access. And something out-bottlenecking disk acess is not something that happens often.
If you write the UI only once per user triggerd event, you will not notice the cost. But do it from any form of loop - especially one running in another thread - and you will start to notice it. I was particualry informed that a foreach is apparently half as slow at ittearting as a for loop.
I even made a example for this, albeit in a Windows Forms Environment:
using System;
using System.Windows.Forms;
namespace UIWriteOverhead
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
int[] getNumbers(int upperLimit)
{
int[] ReturnValue = new int[upperLimit];
for (int i = 0; i < ReturnValue.Length; i++)
ReturnValue[i] = i;
return ReturnValue;
}
void printWithBuffer(int[] Values)
{
textBox1.Text = "";
string buffer = "";
foreach (int Number in Values)
buffer += Number.ToString() + Environment.NewLine;
textBox1.Text = buffer;
}
void printDirectly(int[] Values){
textBox1.Text = "";
foreach (int Number in Values)
textBox1.Text += Number.ToString() + Environment.NewLine;
}
private void btnPrintBuffer_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(10000);
MessageBox.Show("Printing with buffer");
printWithBuffer(temp);
MessageBox.Show("Printing done");
}
private void btnPrintDirect_Click(object sender, EventArgs e)
{
MessageBox.Show("Generating Numbers");
int[] temp = getNumbers(1000);
MessageBox.Show("Printing directly");
printDirectly(temp);
MessageBox.Show("Printing done");
}
}
}
But even this overhead is pretty unlikey to have a presistent result. At some time the read thread should get the lock first, blocking write. But still, there are too many variables to say for sure. You should propably try a simpler example, with more consistent (and a whole lot less) writework. What about writing "A" and "B" to the console, instead of complex stuff like this?

One longer call is causing all parallel loops to pause or blocked

I am calling a VB 6.0 dll in Parallel.ForEach and expecting all calls to be started simultaneously or at least 2 of them based on my PC's cores or threads availability in thread pool
VB6 dll
Public Function DoJunk(ByVal counter As Long, ByVal data As String) As Integer
Dim i As Long
Dim j As Long
Dim s As String
Dim fno As Integer
fno = FreeFile
Open "E:\JunkVB6Dll\" & data & ".txt" For Output Access Write As #fno
Print #fno, "Starting loop with counter = " & counter
For i = 0 To counter
Print #fno, "counting " & i
Next
Close #fno
DoJunk = 1
End Function
counter is being passed from the caller to control execution time of the call and file is being written to make it an IO based process.
C# caller
private void ReportProgress(int value)
{
progressBar.Value = value;
//progressBar.Value++;
}
private void button1_Click(object sender, EventArgs e)
{
progressBar.Value = 0;
counter = 0;
Stopwatch watch = new Stopwatch();
watch.Start();
//var range = Enumerable.Range(0, 100);
var range = Enumerable.Range(0, 20);
bool finished = false;
Task.Factory.StartNew(() =>
{
Parallel.ForEach(range, i =>
{
#region COM CALL
JunkProject.JunkClass junk = new JunkProject.JunkClass();
try
{
Random rnd = new Random();
int dice = rnd.Next(10, 40);
int val = 0;
if (i == 2)
val = junk.DoJunk(9000000, i.ToString());
else
val = junk.DoJunk(dice * 10000, i.ToString());
System.Diagnostics.Debug.Print(junk.GetHashCode().ToString());
if (val == 1)
{
Interlocked.Increment(ref counter);
progressBar.Invoke((Action)delegate { ReportProgress(counter); });
}
junk = null;
}
catch (Exception excep)
{
i = i;
}
finally { junk = null; }
#endregion
});
}).ContinueWith(t =>
{
watch.Stop();
MessageBox.Show(watch.ElapsedMilliseconds.ToString());
});
}
This line is making a specific call longer than the others.
val = junk.DoJunk(9000000, i.ToString());
Here this second process is causing all calls inside the Parallel.ForEach to stop i.e. no other file is created unless this 2nd call gets completed.
Is it an expected behavior or i am doing something wrong?
As #John Wu suggested that you can create AppDomain to allow COM to run on different App Domain, I believe you could run your parallel like this.
Parallel.ForEach(range, i =>
{
AppDomain otherDomain = AppDomain.CreateDomain(i.ToString());
otherDomain.DoCallBack(delegate
{
//Your COM call
});
});
EDIT
Right.. I am not sure how can you set serializable on VB6.0 class. You can try the other way (Marshaling objects by reference). Noted: I haven't actually tested this, but I would like to know if that will work.
Parallel.ForEach(range, i =>
{
AppDomain otherDomain = AppDomain.CreateDomain(i.ToString());
var comCall = (ComCall) otherDomain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, typeof(ComCall).ToString());
comCall.Run();
AppDomain.Unload(otherDomain);
});
and the class
public class ComCall : MarshalByRefObject
{
public void Run()
{
//Your COM Call
}
}
Here is also additional reference regarding the topic.
https://www.codeproject.com/Articles/14791/NET-Remoting-with-an-easy-example

How to make run real time and faster refresh method with timer

I have script for refresh network with object label and panel but in script using looping mode with 'for'. I want to this real time refresh for 1 sec or 5 sec but because using 'for' make this procces need more time and get stuck screen. how to make the solution more quickly and in real time?
Thanks
public PosPing()
{
InitializeComponent();
RefreshPOS.Tick += new EventHandler(CheckPOSUG);
RefreshPOS.Start();
}
private void CheckPOSUG(object sender, EventArgs e)
{
Panel[] panelUG = new Panel[]{pnlPOSUG1,pnlPOSUG2,pnlPOSUG3,pnlPOSUG4,pnlPOSUG5,pnlPOSUG6,pnlPOSUG7,pnlPOSUG8};
Label[] LabelUG = new Label[]{lblUG1,lblUG2,lblUG3,lblUG4,lblUG5,lblUG6,lblUG7,lblUG8};
Label[] lblSpdUG = new Label[] { lblSpdUG1, lblSpdUG2, lblSpdUG3, lblSpdUG4, lblSpdUG5, lblSpdUG6, lblSpdUG7, lblSpdUG8 };
for (int x = 0; x < 8;x++ )
{
string IP = "192.168.135.1" + (x + 1).ToString();
var ping = new Ping();
var reply = ping.Send(IP, 10 * 1000);
LabelUG[x].Text = "POSBMS10" + x.ToString();
if (reply.Status == IPStatus.Success)
{
lblSpdUG[x].Text = reply.RoundtripTime.ToString() + " " + "ms";
panelUG[x].BackColor = Color.FromName("Lime");
}
else
{
lblSpdUG[x].Text = "Nonaktif";
panelUG[x].BackColor = Color.FromName("ButtonHighlight");
}
}
}
Without a good, minimal, complete code example, it's hard to know for sure how to best answer your question. But it looks like you are trying to ping eight different servers, which are represented by eight set of controls in your form.
If that is correct, then I agree with commenter Hans Passant that you should be using the SendPingAsync() method instead. This will allow you to execute the pings asynchronously, without blocking the UI thread, so that your program can remain responsive.
Because you are dealing with eight different servers, it makes sense to me that you should execute the eight pings asynchronously. To accomplish this, I would refactor the code a bit, putting the server-specific loop body into a separate method, so that each instance can be run concurrently.
Implementing it that way would look something like this:
private async void CheckPOSUG(object sender, EventArgs e)
{
Panel[] panelUG = new Panel[]{pnlPOSUG1,pnlPOSUG2,pnlPOSUG3,pnlPOSUG4,pnlPOSUG5,pnlPOSUG6,pnlPOSUG7,pnlPOSUG8};
Label[] LabelUG = new Label[]{lblUG1,lblUG2,lblUG3,lblUG4,lblUG5,lblUG6,lblUG7,lblUG8};
Label[] lblSpdUG = new Label[] { lblSpdUG1, lblSpdUG2, lblSpdUG3, lblSpdUG4, lblSpdUG5, lblSpdUG6, lblSpdUG7, lblSpdUG8 };
Task[] tasks = new Task[8];
for (int x = 0; x < 8; x++)
{
tasks[x] = PingServer(x, panelUG[x], LabelUG[x], lblSpdUG[x]);
}
try
{
await Task.WhenAll(tasks);
}
catch (Exception e)
{
// handle as appropriate, e.g. log and exit program,
// report expected, non-fatal exceptions, etc.
}
}
async Task PingServer(int index, Panel panel, Label ugLabel, Label spdLabel)
{
// NOTE: String concatenation will automatically convert
// non-string operands by calling calling ToString()
string IP = "192.168.135.1" + (index + 1);
var ping = new Ping();
var reply = await ping.SendPingAsync(IP, 10 * 1000);
ugLabel.Text = "POSBMS10" + x;
if (reply.Status == IPStatus.Success)
{
spdLabel.Text = reply.RoundtripTime + " ms";
// The Color struct already has named properties for known colors,
// so no need to pass a string to look Lime up.
panel.BackColor = Color.Lime;
}
else
{
spdLabel.Text = "Nonaktif";
panel.BackColor = Color.FromName("ButtonHighlight");
}
}

How can I get the the waiting times for access or failing in a function that is locked by threads?

I'm using a function to add some values in an Dynamic Array (I know that I could use a list but it's a requirement that I must use an Array).
Right now everything is working but I need to know when a thread fails adding a value (because it's locked and to save that time) and when it adds it (I think when it adds, I already have it as you can see in the function Add.
Insert Data:
private void button6_Click(object sender, EventArgs e)
{
showMessage(numericUpDown5.Value.ToString());
showMessage(numericUpDown6.Value.ToString());
for (int i = 0; i < int.Parse(numericUpDown6.Value.ToString()); i++)
{
ThreadStart start = new ThreadStart(insertDataSecure);
new Thread(start).Start();
}
}
private void insertDataSecure()
{
for (int i = 0; i < int.Parse(numericUpDown5.Value.ToString()); i++)
sArray.addSecure(i);
MessageBox.Show(String.Format("Finished data inserted, you can check the result in: {0}", Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"times.txt")), "Result", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
Function to Add:
private object padLock = new object();
public void addSecure(int value)
{
Stopwatch sw = Stopwatch.StartNew();
string values = "";
lock (padLock)
{
try
{
if (array == null)
{
this.size = 1;
Resize(this.size);
array[0] = value;
count++;
}
else
{
count++;
if (size == count)
{
size *= 2;
Resize(size);
}
array[count - 1] = value;
}
}
catch
{
throw new System.ArgumentException("It was impossible to insert, try again later.", "insert");
}
values=String.Format("Element {0}, Time taken: {1}ms", value.ToString(), sw.Elapsed.TotalMilliseconds);
sw.Stop();
saveFile(values);
}
Sorry for asking this question but I have read different articles and this is the last one that I tried to use: http://msdn.microsoft.com/en-us/library/4tssbxcw.aspx but when I tried to implement in my code finally crashed in an strange error.
I'm afraid I might not completely understand the question. It sounds like you want to know how long it takes between the time the thread starts and when it actually acquires the lock. But in that case, the thread does not actually fail to add a value; it is simply delayed some period of time.
On the other hand, you do have an exception handler, so presumably there's some scenario you expect where the Resize() method can throw an exception (but you should catch only those exceptions you expect and know you can handle…a bare catch clause is not a good idea, though the harm is mitigated somewhat by the fact that you do throw some exception the exception handler). So I can't help but wonder if that is the failure you're talking about.
That said, assuming the former interpretation is correct – that you want to time how long it takes to acquire the lock – then the following change to your code should do that:
public void addSecure(int value)
{
Stopwatch sw = Stopwatch.StartNew();
string values = "";
lock (padLock)
{
// Save the current timer value here
TimeSpan elapsedToAcquireLock = sw.Elapsed;
try
{
if (array == null)
{
this.size = 1;
Resize(this.size);
array[0] = value;
count++;
}
else
{
count++;
if (size == count)
{
size *= 2;
Resize(size);
}
array[count - 1] = value;
}
}
catch
{
throw new System.ArgumentException("It was impossible to insert, try again later.", "insert");
}
sw.Stop();
values = string.Format(
"Element {0}, Time taken: for lock acquire: {1}ms, for append operation: {2}ms",
value.ToString(),
elapsedToAcquireLock.TotalMilliseconds,
sw.Elapsed.TotalMilliseconds - elapsedToAcquireLock.TotalMilliseconds);
saveFile(values);
}
}
That will display the individual times for the sections of code: acquiring the lock, and then actually adding the value to the array (i.e. the latter not including the time taken to acquire the lock).
If that's not actually what you are trying to do, please edit your question so that it is more clear.

Cross Thread Problem... Help Me

i have 2 script 1- Soket.cs (Soket Server Working with thread) FormMain.cs(Working Normal) i call a function in FormMain from soket.cs with this code :
public void ResiveFunc(string FuncResive)
{
string FuncName = "";
string FuncValue = "";
for (int i = 0; i <= 2; i++)
{
FuncName += FuncResive[i];
}
for (int j = 4; j <= FuncResive.Length - 1; j++)
{
FuncValue += FuncResive[j];
}
MessageBox.Show(FuncName);
MessageBox.Show(FuncValue);
if (FuncName == "TAB")
{
Form1 mainForm = new Form1();
mainForm.AdverFilter(FuncValue);
}
FuncName = "";
FuncValue = "";
}
i call this AdverFilter() function in FormMain :
public void AdverFilter(string value)
{
Messagebox.show(value);
this.richTextBox1.Text = value;
}
but its dont work!!! Messagebox show fine the value but richtextbox is null...
please tell me how can i fix this problem...!?
Update:
I changed my code to the following:
public void AdverFilter(string value)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action<string>(AdverFilter),value);
}
else
{
MessageBox.Show(value);
this.richTextBox1.Text = value;
}
}
This code has no syntax errors, but it still doesn't work. I added MessageBox.Show(richTextBox1.Text); to this code. It shows true but my Richtextbox is null.
EDIT: Ok, that additional code cleared things a bit.
The callback is most likely done through ThreadPool and so the thread's apartment state is MTA. This is a problem because you are creating the form in the callback's thread and the forms require that the apartment state must be STA. Maybe you should create the form in the main thread and then use the ISynchronizeInvoke interface as shown by Fredrik Mörk.
PS. You should use System.String.Substring(...) to get the FuncName and FuncValue:
string FuncName = FuncResive.Substring(0, 3);
string FuncValue = FuncResive.Substring(4);
And why the FuncName and FuncValue values are set to empty string in the end? There is no reason for that.

Categories