I have a problem that is when I call an async method, it enters and comes up against a condition that I have set, where it sends me an exception that says:
"The subprocess making the call can not access this object because the
owner is another thread.",
I just want the condition process to run in the background with the async method
private Task ObtenerDatosd()
{
return Task.Run(() =>
{
for (int i = draw2.Count - 1; i >= 0; i--)
{
if (draw2[i].ToString().ToLower().Contains(SearcInterno.Text.ToLower()))
{
//action
System.Windows.MessageBox.Show("Code action");
}
}
});
}
You cannot access WPF objects from another thread without a dispatcher. But you don't need it in this case, if you just use:
private Task ObtenerDatosd()
{
string text = SearcInterno.Text.ToLower();
return Task.Run(() =>
{
for (int i = draw2.Count - 1; i >= 0; i--)
{
if(draw2[i].ToString().ToLower().Contains(text))
{
//action
System.Windows.MessageBox.Show("Code action");
}
}
});
}
Related
So, i was making a discord bot that would be able to change a role's colour every after a certain amount of time. it ran properly, however, the bot is unable to register other commands or run any other work while this task is being run. (it wont stop when the stop command is run as it couldn't get in there in the first place).
i was hoping someone here could help fix this problem.
so here's part of the code:
[Group("rainbow")]
public class Rainbow : ModuleBase<SocketCommandContext>
{
public Boolean enabled;
[Command, RequireUserPermission(GuildPermission.ManageRoles)]
public async Task rainbowAsync(SocketRole role, int milliseconds)
{
if (milliseconds >= 500) {
enabled = true;
while (enabled)
{
Random rnd = new Random();
double id = rnd.NextDouble() * 7;
if (id < 1)
{
await role.ModifyAsync(a => a.Color = Discord.Color.Red);
}
else if (id < 2)
{
await role.ModifyAsync(a => a.Color = Discord.Color.Orange);
}
else if (id < 3)
{
await role.ModifyAsync(a => a.Color = Discord.Color.LightOrange);
}
else if (id < 4)
{
await role.ModifyAsync(a => a.Color = Discord.Color.Green);
}
else if (id < 5)
{
await role.ModifyAsync(a => a.Color = Discord.Color.Blue);
}
else if (id < 6)
{
await role.ModifyAsync(a => a.Color = Discord.Color.DarkBlue);
}
else if (id < 7)
{
await role.ModifyAsync(a => a.Color = Discord.Color.Purple);
}
await Task.Delay(milliseconds);
}
}
else { await ReplyAsync("Time value must be higher than 500ms"); }
}
[Command("stop"), RequireUserPermission(GuildPermission.ManageRoles)]
public async Task StopAsync()
{
enabled = false;
await ReplyAsync("Rainbow effect stopped");
}
}
As Alex mentioned in his comment,
Your command goes into an infinite loop.
if (milliseconds >= 500) {
enabled = true;
while (enabled)
{
//Code
await Task.Delay(milliseconds);
}
}
This puts your command into an infinite loop which prevents other commands from firing. The reason this prevents other commands from firing in an Async environment is because Async is still just a single thread and the default way Discord.net calls your command is await Command();
When an async call is awaited it blocks the call until the Task completes. Your task will never complete because of the infinite loop.
There are a couple different ways to handle this, but the way I would suggest to handle it is to schedule the recoloring. Look into ways to schedule tasks to happen every x time-frame.
As an example
public class TimerTask
{
private readonly Timer _taskTimer;
public TimerTask(Action action, int interval = 10000)
{
_taskTimer = new Timer { AutoReset = true, Interval = interval };
_taskTimer.Elapsed += (_, __) => action();
}
public void Start() { _taskTimer.Start(); }
public void Stop() { _taskTimer.Stop(); }
}
Below is a function I have running in a while(true) loop in a thread running a Winforms GUI.
I have a button set to put text data into the inBuffer object. this always works, however when I place into the buffer object from a different thread, the data is detected, pulled, and printed out in the Console.out.WriteLine statement, however it never shows up in the Display (textBox) object
public void put()
{
string strDisplayMe = ModemKind.MainClass.inBuffer.MkRequest();
if (strDisplayMe != "")
{
Console.Out.WriteLine("FOUND SOMETHING IN BUFFER: " + strDisplayMe);
char[] DisplayMeArr = strDisplayMe.ToCharArray ();
for (int i = 0; i <= DisplayMeArr.Length -1; ++i)
{
this.display.Text += DisplayMeArr [i];
Thread.Sleep (100);
}
this.display.Text += '\n';
}
}
EDIT: this is a separate class from what is feeding it data through the static buffer objects
Only the main window thread can access/change controls... if you want update the UI from the thread that runs the loop, you need to sync the call with the main thread using the Control.Invoke method.
For instance...
public void put()
{
string strDisplayMe = ModemKind.MainClass.inBuffer.MkRequest();
if (strDisplayMe != string.Empty)
{
char[] DisplayMeArr = strDisplayMe.ToCharArray();
for (int i = 0; i <= DisplayMeArr.Length -1; ++i)
{
this.UpdateUI(() => { this.display.Text += DisplayMeArr[i]; });
Thread.Sleep(100);
}
this.UpdateUI(() => { this.display.Text += '\n'; });
}
}
private void UpdateUI(Action handler)
{
this.Invoke(handler);
}
You can use MethodInvoker to access TextBox from other thread then GUI thread. Make a method to access the TextBox using MethodInvoker. you are assigning text multiple times consider assigning it once for performance, and use StringBuilder for string concatenation.
public void put()
{
string strDisplayMe = ModemKind.MainClass.inBuffer.MkRequest();
if (strDisplayMe != "")
{
Console.Out.WriteLine("FOUND SOMETHING IN BUFFER: " + strDisplayMe);
char[] DisplayMeArr = strDisplayMe.ToCharArray ();
for (int i = 0; i <= DisplayMeArr.Length -1; ++i)
{
AssignToTextBox(this.display, this.display.Text + DisplayMeArr [i]);
Thread.Sleep (100);
}
AssignToTextBox(this.display, this.display.Text + '\n');
}
}
void AssignToTextBox(TextBox txtBox, string value)
{
if(txtBox.InvokeRequired)
{
txtBox.Invoke(new MethodInvoker(delegate { txtBox.text = value; }));
}
}
Edit You can use BeginInvoke instead of Invoke to call it asynchronously. Read more about the Invoke and BeginInvoke in this question.
void AssignToTextBox(TextBox txtBox, string value)
{
txtBox.BeginInvoke(new Action(()=>txtBox.Text = value ));
}
I have a task, but processes inside it don't run in parallel. Second one waits for the first one to compelete. Can you explain why and how can I correct this? I want both of them run at the same time.
And second question, Should I use task instead of threads?
Thanks in advance.
new Task(() =>
{
Counter1();
Counter2();
}).Start();
private void Counter2()
{
for (int i = 0; i < 30; i++)
{
Thread.Sleep(500);
label2.Text = i.ToString();
}
}
private void Counter1()
{
for (int i = 0; i < 30; i++)
{
Thread.Sleep(500);
label3.Text = i.ToString();
if(i == 15)
Thread.Sleep(3000);
}
}
Use Parallel.Invoke and call Counter1() and Counter2() as shown in following example from MSDN (after updating the () anonymous lambda invocation to invoke your 2 methods.
#region ParallelTasks
// Perform three tasks in parallel on the source array
Parallel.Invoke(() =>
{
Console.WriteLine("Begin first task...");
GetLongestWord(words);
}, // close first Action
() =>
{
Console.WriteLine("Begin second task...");
GetMostCommonWords(words);
}, //close second Action
() =>
{
Console.WriteLine("Begin third task...");
GetCountForWord(words, "species");
} //close third Action
); //close parallel.invoke
Console.WriteLine("Returned from Parallel.Invoke");
#endregion
I want to update a progressbar as each task is completed below.
The method var continuation2 = Task.Factory.ContinueWhenAny(..... doesnt work.
What is the correct way to do this?
C# Code
private void radButtonInsertManyErrors_Click(object sender, EventArgs e)
{
try
{
radProgressBarStatus.Maximum = int.Parse(radTextBoxNumberofErrorsInsert.Text);
radProgressBarStatus.Value1 = 0;
Task<int>[] tasks = new Task<int>[int.Parse(radTextBoxNumberofErrorsInsert.Text)];
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
{
int x = i;
tasks[i] = new Task<int>(() =>
{
//insert the error into table FA_Errors
Accessor.Insert_FAErrors(BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login.ToString(),
(int)BLLErrorCodes.BLL_Error_Codes.Error_Log_Event_Login,
"Some Error", "",
MethodBase.GetCurrentMethod().DeclaringType.Namespace.ToString(),
MethodBase.GetCurrentMethod().Name.ToString(),
BLLErrorCategory.BLL_Error_Category.WEB_APP.ToString(),
"pc source", "damo",
sConn.ToString());
return 1;
});
}
var continuation = Task.Factory.ContinueWhenAll(
tasks,
(antecedents) =>
{
RadMessageBox.Show("Finished inserting errors ");
});
var continuation2 = Task.Factory.ContinueWhenAny(
tasks,
(antecedents) =>
{
radProgressBarStatus.Value1++;
});
for (int i = 0; i < int.Parse(radTextBoxNumberofErrorsInsert.Text); i++)
tasks[i].Start();
// Use next line if you want to block the main thread until all the tasks are complete
//continuation.Wait();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
You can use this function:
public static void TaskProgress(IEnumerable<Task> tasks, Action<int> callback)
{
int count = 0;
foreach (var task in tasks)
task.ContinueWith(t => callback(Interlocked.Increment(ref count)));
}
It will call the callback each time a task completes with the number of currently completed tasks. Note that the callbacks are not synchronized, so it can be called while the previous callback is still running.
Set up a continuation with each of the tasks. Keep a (thread-safe) counter on how many completed and update the UI on completion of each task.
Actually, Task.WhenAll does keep such a counter under the hood. It is just not accessible.
i'm using the BlockingCollection for a Producer Consumer pattern and i got an excecption i think to write a patent on it- only two results in google!
the expection is "CompleteAdding may not be used concurrently with additions to the collection" and it happens when i TryAdd on th BlockingCollection as Follows:
public void EnqueueTask(T item)
{
if (!_cancellationTokenSource.IsCancellationRequested)
{
_workerQueue.Add(item);
}
}
the CompleteAdding is called on the dispose of the Consumer-Producer wrapper class:
public void Dispose()
{
if (!_IsActive)
return;
_IsActive = false;
_cancellationTokenSource.Cancel();
_workerQueue.CompleteAdding();
// Wait for the consumer's thread to finish.
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000))
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
catch (Exception ex)
{
OnLogged(ex.Message + ex.StackTrace);
}
});
}
// Release any OS resources.
}
Anyone from microsoft got an idea? should i sleep after the cancelation and before calling the CompleteAdding?
Look at this piece of the code:
for (int i = 0; i < _workers.Length; ++i)
{
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[i].Join(4000)) << == Here
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In _workers[i].Join(4000), the value of i is not what you think it is. Try again with:
for (int i = 0; i < _workers.Length; ++i)
{
int j = i; // copy
Task t1 = Task.Factory.StartNew(() =>
{
try
{
if (!_workers[j].Join(4000)) // j
LogWriter.Trace("Failed to join thread", "ThreadFailureOnDispose");
}
In your version, the variable 'i' is captured and all Tasks use the same var. All but the first few will see i == _workers.Length because they are executed after the for-loop is completed.
It is a classic lambda + captured var problem.