good morning
am using C# to develop pc software
and am using this method in my software to delay loop between lines
System.Threading.Thread.Sleep(x);
but this make my software freeze .
how to do this method work with out freeze my application
my full code
private void button18_Click_3(object sender, EventArgs e)
{
for (int i = 0; i < lstgroups.Items.Count; i++)
{
//
System.Threading.Thread.Sleep(x);
System.Windows.Forms.Application.EnableVisualStyles();
//
timeless.Enabled = true;
timeless.Start();
duration--;
if (duration == 0)
{
timeless.Start();
}
if (Gsend.Post(lstgroupsbox.Items[i].ToString(), AppSettings.Default.AccessToken, txtStatus.Text, txtLink.Text) == true)
lblshow.Text = "Sent To : " + lstgroups.Items[i].Text;
lblsendid.Text = "Sent To : " + lstgroupsbox.Items[i];
{
//foreach (var item in lstgroups.Items)
//{
// if (Gsend.Post(item.ToString(), "", txtStatus.Text, "", 2, "", txtImagePath.Text) == true)
// MessageBox.Show("Post Has Been Sent");
//}
}
Avoid using Thread.sleep() as it will put the Main thread on hold for given time and your GUI will freeze.
Other Option
Start Timer with interval you needed (X). and it should be fine.
Use background worker thread, as sleep in background worker will not effect your main GUI.
Do the work on a thread that isn't the main thread. By using sleep in an event you are essentially halting any processing on the main thread.
Maybe you can use a Background Worker
Background Worker
Related
I'm working with a WinForm from which all processes that I need are steered. Now I'm trying to integrate a BackgroundWorker with a ProgressBar and a cancellation button into my code. I want it to be locally around my code and not in a separate method. To test this, a new form is created with a progress bar (not active yet) and a button to stop a for-loop. However, the code is not working (and the progress bar is not even included yet). The form freezes immediately (see image) so I can't test the cancel button. The for-loop, however, is executed and "Done: " + l.ToString() is shown. How can I solve this?
void stopMeasurement(object sender, EventArgs e)
{
stopMeas = true;
}
public void testcancel() // Test method which is triggered manually
{
int l = 0;
MetingProgress metingProgress = new MetingProgress();
metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement);
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += (sender, args) =>
{
for (int k = 0; k < 10; k++)
{
Thread.Sleep(1000);
l++;
if (worker.CancellationPending)
break;
}
MessageBox.Show("Done: " + l.ToString());
};
worker.RunWorkerAsync();
while (worker.IsBusy)
{
if (stopMeas)
worker.CancelAsync();
}
metingProgress.Dispose();
MessageBox.Show("All done");
}
The form freezes immediately
this is because you have a while loop still running on the main thread! So the form will not be responsive. This is called buisy waiting. You will not be able to call the CancelAsync method.
One solution could be to remove the while-loop and place the cancel call into the button event code:
void stopMeasurement(object sender, EventArgs e)
{
stopMeas = true;
worker.CancelAsync();
}
What you have basically done is: you created a second cancelation token. So another possibility could be to use only stopMeas to cancel the background operation:
worker.DoWork += (sender, args) =>
{
for (int k = 0; k < 10; k++)
{
Thread.Sleep(1000);
l++;
if (stopMeas)
break;
}
string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!";
MessageBox.Show(mes);
};
EDIT: also this line:
metingProgress.Dispose();
might lead to an ObjectDisposed exception. If the background process is still running and trying to update your progressbar and you already dispose the form. You should remove this line and leave it to the garbage collector.
This code is your problem:
while (worker.IsBusy)
{
if (stopMeas)
worker.CancelAsync();
}
Your GUI-Thread is in that loop until your worker is done.
You need to make your worker instance be reachable from within the EventHandler and call worker.CancelAsync() from there.
Outside this , I personally would improve the code in 2 steps:
Move the whole BackgroundWorker into the MetingProgress class and make its constructor take a delegate for the actual work implementation.
Use TAP (Task Async Pattern) , i.e. async/await Task with Progress and CancellationToken.
I'm working on a program that's supposed to establish "n" many SSH connections with a remote Linux server, and run time consuming commands on each connection. The "time consuming operation" is basically running a script that sets up Wireshark and listens to the traffic.
For this I'm using the SharpSSH library for C# and n many BackgroundWorkers as threads. Also for simplicity, the code below has n=2 BGW threads and SSH connections.
Code:
// runs when start is pressed
private void startButton_Click_1(object sender, EventArgs e)
{
sb = new StringBuilder();
DateTime timeNow = DateTime.Now;
clickTime = timeNow.ToString("yyyyMMddhhmmssfff"); // store the exact time of the click
bw = bwArray[0];
int index = 0; // ignore these 2 constants
foreach (BackgroundWorker bgw in bwArray)
{
if (bgw.IsBusy != true)
{
bgw.RunWorkerAsync();
// runWorkerAsync for every BackgroundWorker in the array
//index++;
}
}
}
// runWorkerAsync leads the BGWorker to this function
private void bw_doWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
// let the UI know of button changes
int p = 0;
object param = "something"; // use this to pass any additional parameter back to the UI
worker.ReportProgress(p, param);
// UI notifying part ends here
// for the simplex case
if (numberOfConnections == 1)
startOperation();
// for the multiplex case
else if (numberOfConnections > 1)
{
//while (p < numberOfConnections)
//{
multiStartOperation();
// p++;
//}
}
Thread.Sleep(500);
}
}
// will be called for all ssh connections (in multiplex case)
private void multiStartOperation()
{
string[] command1Array = { "host2", "host2" };
string[] command2Array = { clickTime + "_h2", clickTime + "_h2" };
for (int index = 0; index < numberOfConnections; index++)
{
// shellArray is an array of SshExec objects
shellArray[index] = new SshExec(IPAddress, username, password);
try
{
shellArray[index].Connect();
}
catch (JSchException se)
{
Console.Write(se.StackTrace);
System.Windows.Forms.MessageBox.Show("Couldn't connect to the specified port.", "Connection Error!");
}
sb.Append(shellArray[index].RunCommand(command1Array[index]) + Environment.NewLine);
// first command is host3, or host4 etc.
// below is the time consuming command to run
string command = "./logcap.sh -c " + command2Array[index] + " -z";
// sb is a global stringBuilder object,
// to which the command output is appended
sb.Append(shellArray[index].RunCommand(command));
}
}
My problem is the following:
When I press the start button on the GUI, both connections should start and run the script. Whereas in the code given above, the first index of shellArray (which consists of SshExec objects) gets connected, prepares the commands and runs the time consuming command, at which point the program goes back to the UI, without even starting the second connection. This is obviously because of the for loop, but I couldn't figure out how to work around this yet.
I need to get the other backgroundworker to establish and run the second command with the second server, so that when I press the stop button on the GUI all connections and threads can stop all together.
PS: The commands will not stop running unless the user clicks stop, which sends a Ctrl-C signal to the server.
I'm relatively new to all the multithreading and networking concepts, so if there is any confusion or mistake please let me know.
Have a nice day.
Thank you for your answers, and the welcome. :)
The problem indeed was not being able to run multiple backgroundworkers at the same time.
I managed to solve the issue. It turns out that all I had to figure out was how to assign backgroundworkers to SSH connections. To do that, I created a class as follows:
class BGW
{
private BackgroundWorker bgw;
private int index;
//getters, setters, constructors...
}
After this, I converted bwArray which was an array of BackgroundWorkers into an array of BGW objects. At initialization, I assigned each BGW object an index.
Instead of having the stupid loop within multiStartOperation(), I sent an integer parameter to multiStartOperation() and that function used that index to reach the allocated backgroundworker.
So far it seems to work.
Have a nice day.
I have a button that on click event I get some information from the network.
When I get information I parse it and add items to ListBox. All is fine, but when I do a fast double-click on button, it seems that two background workers are running and after finishing all work, items in the list are dublicated.
I want to do so that if you click button and the proccess of getting information is in work, this thread is stopping and only after first work is completed the second one is beginning.
Yes, I know about AutoResetEvent, but when I used it it helped me only one time and never more. I can't implement this situation and hope that you will help me!
Now I even try to make easier but no success :( : I added a flag field(RefreshDialogs)(default false), when the user clicks on button, if flag is true(it means that work is doing), nothing is doing, but when flag field is set to false, all is fine and we start a new proccess.
When Backgroundwork completes, I change field flag to false(it means that user can run a new proccess).
private void Message_Refresh_Click(object sender, EventArgs e)
{
if (!RefreshDialogs)
{
RefreshDialogs = true;
if (threadBackgroundDialogs.WorkerSupportsCancellation)
{
threadBackgroundDialogs.CancelAsync();
}
if (!threadBackgroundDialogs.IsBusy)
{
downloadedDialogs = 0;
threadBackgroundDialogs = new BackgroundWorker();
threadBackgroundDialogs.WorkerSupportsCancellation = true;
threadBackgroundDialogs.DoWork += LoadDialogs;
threadBackgroundDialogs.RunWorkerCompleted += ProcessCompleted;
threadBackgroundDialogs.RunWorkerAsync();
}
}
}
void ProcessCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RefreshDialogs = false;
}
So you want to keep the second process running while the first works, but they shouldn't disturb each other? And after the first one finishes the second one continues?
Crude way: While loop:
if (!RefreshDialogs)
{
RefreshDialogs = true;
this becomes:
while(RefreshDialogs)
{
}
RefreshDialogs = true;
After you set it false the second process wwill jump out of the while. (Note this is extremly inefficent since both processes will be running all the time, i'm pretty sure the second one will block the first one, but with multitasking now it shouldn't, if it block use a Dispatcher.Thread)
Elegant way: Use A Semaphore
http://msdn.microsoft.com/de-de/library/system.threading.semaphore%28v=vs.80%29.aspx
If you find it impossible to have both processes running at the same time, or want another way:
Add an Array/List/int and when the second process notices there is the first process running, like with your bool, increase your Added variable, and at the end of the process, restart the new process and decrese the variable:
int number;
if (!RefreshDialogs)
{
RefreshDialogs = true;
your code;
if(number > 0)
{
number--;
restart process
}
}
else
{
number++;
}
I have to admit, i like my last proposal the most, since its highly efficent.
Make your thread blocking. That is easy;
lock(someSharedGlobalObject)
{
Do Work, Exit early if cancelled
}
This way other threads will wait until the first thread releases the lock. They will never execute simultaneously and silently wait until they can continue.
As for other options; why not disable the button when clicked and re-enable it when the backgroundworker completes. Only problem is this does not allow for cancelling the current thread. The user has to wait for it to finish. It does make any concurrency go away very easily.
How about this approach?
Create a request queue or counter which will be incremented on every button click. Every time that count is > 0. Start the background worker. When the information comes, decrement the count and check for 0. If its still > 0 restart the worker. In that your request handler becomes sequential.
In this approach you may face the problem of continuous reference of the count by two threads, for that you may use a lock unlock condition.
I hav followed this approach for my app and it works well, hope it does the same for you.
I'm not an Windows Phone expert, but as I see it has support for TPL, so following code would read nicely:
private object syncRoot =new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
latestTask = Task.Factory.StartNew(action);
else
latestTask = latestTask.ContinueWith(tsk => action());
}
}
Use can use semaphores
class TheClass
{
static SemaphoreSlim _sem = new SemaphoreSlim (3);
static void Main()
{
for (int i = 1; i <= 5; i++)
new Thread (Enter).Start (i);
}
static void Enter (object name)
{
Console.WriteLine (name + " wants to enter");
_sem.Wait();
Console.WriteLine (name + " has entered!");
Thread.Sleep (1000 * (int) name );
Console.WriteLine (name + " is leaving");
_sem.Release(); }
}
}
I found the solution and thanks to #Giedrius. Flag RefreshingDialogs is set to true only when proccess is at the end, when I added items to Listbox. The reason why I'am using this flag is that state of process changes to complete when the asynchronous operation of getting content from network(HttpWebRequest, method BeginGetRequestStream) begins, but after network operaion is complete I need to make UI operations and not only them(parse content and add it to Listbox)My solution is:
private object syncRoot = new object();
private Task latestTask;
public void EnqueueAction(System.Action action)
{
lock (syncRoot)
{
if (latestTask == null)
{
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
else if(latestTask.IsCompleted && !RefreshingDialogs)
{
RefreshingDialogs = true;
downloadedDialogs = 0;
latestTask = Task.Factory.StartNew(action);
}
}
}
private void Message_Refresh_Click(object sender, EventArgs e)
{
Action ac = new Action(LoadDialogs2);
EnqueueAction(ac);
}
Is there such a function like sleep(seconds) but it wouldn't block UI updates?
I have a code like this and if I put threading sleep after (letters.Children[Words[index].index] as TextBlock).Text = Words[index].LetterCorrect; (I want to sleep after that) it just waits 1 sec and then UI gets updates, but I dont want that.
private void Grid_Click(object sender, RoutedEventArgs e)
{
if (index == Words.Count() - 1) return;
if ((((e.Source as Button).Content as Viewbox).Child as Label).Content.ToString() == Words[index].LetterCorrect)
{
(letters.Children[Words[index].index] as TextBlock).Text = Words[index].LetterCorrect;
letters.Children.Clear();
LoadWord(++index);
this.DataContext = Words[index];
}
}
Try a Timer and have the Elapsed callback execute the code you want to happen after the one second.
Create a working thread that does the work for you and let that thread sleep for the desired time before going to work
e.g.
ThreadPool.QueueUserWorkItem((state) =>
{
Thread.Sleep(1000);
// do your work here
// CAUTION: use Invoke where necessary
});
Put the logic itself in a background thread separate from the UI thread and have that thread wait.
Anything in the UI thread that waits 1 second will lock up the entire UI thread for that second.
Use an async scheduled callback:
private void Grid_Click(object sender, RoutedEventArgs e)
{
if (index == Words.Count() - 1) return;
if ((((e.Source as Button).Content as Viewbox).Child as Label).Content.ToString() == Words[index].LetterCorrect)
{
(letters.Children[Words[index].index] as TextBlock).Text = Words[index].LetterCorrect;
Scheduler.ThreadPool.Schedule(schedule =>
{
letters.Children.Clear();
LoadWord(++index);
this.DataContext = Words[index];
}, TimeSpan.FromSeconds(1));
}
}
Not sure what framework you are using, but if you are using Silverlight or WPF, have you considered playing an animation that reveals the correct letter or does a fade sequence that takes 1000ms?
I'm attempting a very DB intensive task in a project. Here is a walk-through:
We need to search our DB of workers, we called Locums, and find one for a specific job. This procedure starts when we decide to process x number of jobs. So, on the click of a button, we process using the ProcessJobBatch() method. However, this method only process against a very limited number of Locums. So it takes less then 10 seconds to fill up a scheduler control. Now, once the limited number of Locums are served, we need to run a background task to check the rest of the Locums. There are around 1250 of them!
So, once ProcessJobBatch() finishes, a BackgroundWorker, BackgroundWorkerMoreLocums, goes off. Now, this worker basically does a simple loop: For each job, go through the whole 1250 employees. This takes way too long. I need to plan this out using an alternate strategy that I can't of ATM or I need to show a secondary progress bar for the inner for-each loop.
More Explanation: We import a batch of Jobs (10 to 70) numerous times on daily bases. Once a batch is imported, the application instructs the logged-in user to "Preference Find" those newly created jobs. The user already has a list of his favorite locums (1 to 20). He wants to distribute the jobs among his favorites first. That is done through ProcessJobBatch(). But, there are two scenarios that prevent the flow there and then:
What if certain jobs didn't fall to
any favorite locum?
What if there is a locum in the whole
DB who can do almost all the jobs but
since he isn't favorite?
So, I end up with a scenario of matching a job with each Locum.
Question:
Can second BackgroundWorker run within a BackgroundWorker's DoWork?
Am I doing the second scan wrong?
Environment: Windows 7 Pro 64-bit, Visual Studio 2010, C#, .NET 4.0, and Windows Forms
private void ButtonPreferenceFind_Click(object sender, EventArgs e) {
if (LookUpBatches.EditValue != null) {
JobBatch JobBatchSelected = DbContext.JobBatches.FirstOrDefault(job_batch=> job_batch.OID == LookUpBatches.EditValue.ToString());
if (JobBatchSelected != null && JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1) > 0) {
if (XtraMessageBox.Show(String.Format("Are you sure to process {0} job(s)?", JobBatchSelected.Jobs.Count(condition => condition.JobStatusID == 1)), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {
ProcessJobBatch(JobBatchSelected);
IEnumerable<Job> SpecificJobs = from req_jobs in JobBatchSelected.Jobs
where req_jobs.JobStatusID == 1
select req_jobs;
ProgressBarControlPreferenceFinder.EditValue = 0;
ProgressBarControlPreferenceFinder.Properties.Minimum = 0;
ProgressBarControlPreferenceFinder.Properties.Maximum = SpecificJobs.Count() - 1;
BackgroundWorkerMoreLocums.RunWorkerAsync(SpecificJobs);
} else {
LookUpBatches.Focus();
}
} else {
XtraMessageBox.Show("Unable to retrieve the selected batch or the batch has no processable jobs.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
LookUpBatches.Focus();
}
} else {
XtraMessageBox.Show("Select a batch first.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
LookUpBatches.Focus();
}
}
#region Background Searching
private void BackgroundWorkerMoreLocums_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
try {
e.Result = GetTableData(e.Argument);
}
catch (Exception ex) {
XtraMessageBox.Show("Background Error: " + ex.Message, "Excite Engine 2", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Result = ex;
}
}
private void BackgroundWorkerMoreLocums_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
// only display progress, do not assign it to grid
ProgressBarControlPreferenceFinder.Increment(e.ProgressPercentage);
}
private void BackgroundWorkerMoreLocums_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
if (e.Result is DataTable) {
//dataGridView1.DataSource = e.Result as DataTable;
}
else if (e.Result is Exception) {
}
}
private DataTable GetTableData(Object JobList) {
DataTable ResultDataTable = new DataTable();
ResultDataTable.Columns.Add();
IEnumerable<Job> JobBatchSelected = (IEnumerable<Job>)JobList;
IEnumerable<Locum> LeftOverLocums = from lefties in DbContext.Locums
//where SchedulerMatrixStorage.Resources.Items.Select(res => (long)res.Id).ToList().Contains(lefties.OID) == false
select lefties;
int NumOfJobsProcessed = 0;
List<KeyValuePair<long, TemporaryPreferenceFindLocum>> AlreadyPrefferedLocums = new List<KeyValuePair<long, TemporaryPreferenceFindLocum>>();
foreach (Job oneJob in JobBatchSelected) {
foreach (Locum oneLocum in LeftOverLocums) {
if (DbContext.Availabilities.Any(check => check.LocumID == oneLocum.OID && check.AvailableDate == oneJob.JobDate && check.AvailabilityStatusID == 1)) {
//This Locum can do this job
//Now check if he/she has been just alloted
if (AlreadyPrefferedLocums.Any(search => search.Key == oneLocum.OID && search.Value.JobDate == oneJob.JobDate) == false) {
//No? Cool!
//Add to the list to prevent double allocation
AlreadyPrefferedLocums.Add(new KeyValuePair<long, TemporaryPreferenceFindLocum>(oneJob.OID, new TemporaryPreferenceFindLocum(oneJob.JobDate, oneJob.OID, oneLocum.OID, oneLocum.FirstName + " " + oneLocum.LastName)));
}
else {
continue;
}
}
else {
//Not marked as Avaliable on the required job date...
continue;
}
}
NumOfJobsProcessed++;
BackgroundWorkerMoreLocums.ReportProgress((int)(NumOfJobsProcessed * 100F / (JobBatchSelected.Count() - 1)));
}
return ResultDataTable;
}
#endregion
A BackgroundWorker can be started from within the DoWork handler of another BackgroundWorker, but you need to be aware of the consequences of using such a scheme. When you start a background worker from your main UI thread the DoWork handler is executed on a thread pool thread while the ProgressChanged and RunWorkerCompleted are executed back on the main UI thread making it safe for you to interact with windows forms controls.
This scenario is guaranteed when you start the worker from the main UI thread because it picks up the SynchronizationContext available on that thread and which is initialized by the windows forms infra-structure.
However, when you start a background worker from the DoWork handler of another worker, you'll be starting it from a thread pool thread that lacks the synchronization context causing the ProgressChanged and RunWorkerCompleted handlers to also be executed on thread pool threads and not in your main UI thread making it unsafe for you to interact with windows forms controls from within those handlers.
It is quite common to have one background thread spawn new background threads. I don't think it is a problem if you scan the list on a background thread and process each list item on another thread.
In such cases there is no background worker within another. There is just a background worker starting other threads.
Things you should consider -
Be aware of what you do in the completed event handlers in case you handle that event.
Consider the performance implications of running so many threads for small tasks. You should consider using PLINQ or parallel tasks so that .Net can handle the partitioning of input and merging of results giving you optimum performance.