I have this:
public class ServiceLibrary
{
public object result = null;
private bool finished = false;
public void testar()
{
ServiceReference.Service1SoapClient serviceReference = new ServiceReference.Service1SoapClient();
serviceReference.updateUserCompleted += new EventHandler<ServiceReference.updateUserCompletedEventArgs>(serviceReference_updateUserCompleted);
serviceReference.updateUserAsync();
ManualResetEvent m = new ManualResetEvent(true);
}
void serviceReference_updateUserCompleted(object sender, ServiceReference.updateUserCompletedEventArgs e)
{
result = e.Result;
finished = true;
}
}
and outside I have this:
public Home()
{
InitializeComponent();
ServiceLibrary serviceLibrary = new ServiceLibrary();
serviceLibrary.testar();
lblCharName.Text = Convert.ToString(serviceLibrary.result);
}
What should I do to the thread wait, so when I assign the text, it contains the value, please?
Thank you
Could you not use your ManualResetEvent? or create a fresh one.
I believe the ManualResetEvent is thread safe....
public class ServiceLibrary
{
public object result = null;
private bool finished = false;
public ManualResetEvent m;
public void testar()
{
ServiceReference.Service1SoapClient serviceReference = new ServiceReference.Service1SoapClient();
serviceReference.updateUserCompleted += new EventHandler<ServiceReference.updateUserCompletedEventArgs>(serviceReference_updateUserCompleted);
serviceReference.updateUserAsync();
m = new ManualResetEvent(false);
}
void serviceReference_updateUserCompleted(object sender, ServiceReference.updateUserCompletedEventArgs e)
{
result = e.Result;
finished = true;
m.Set();
}
}
public Home()
{
InitializeComponent();
ServiceLibrary serviceLibrary = new ServiceLibrary();
serviceLibrary.testar();
serviceLibrary.m.WaitOne();
lblCharName.Text = Convert.ToString(serviceLibrary.result);
}
Make your ManualResetEvent variable m a member variable of the class.
and inside your threaded method: serviceReference_updateUserCompleted, make sure to call
m.WaitOne();
What about
public class ServiceLibrary
{
public object result = null;
public void testar()
{
var serviceReference = new ServiceReference.Service1SoapClient();
using(var m = new ManualResetEvent(false))
{
Action<object, ServiceReference.updateUserCompletedEventArgs> handler =
(sender, e) =>
{
result = e.Result;
m.Set();
};
serviceReference.updateUserCompleted += handler;
serviceReference.updateUserAsync();
m.WaitOne();
serviceReference.updateUserCompleted -= handler;
}
}
}
I have a similar situation. I use a technique called polling, which is exactly what it sounds like. It may or may not be appropriate for you depending on your situation.
public class ServiceLibrary
{
public object result = null;
private bool finished = false;
public void testar()
{
ServiceReference.Service1SoapClient serviceReference = new ServiceReference.Service1SoapClient();
serviceReference.updateUserCompleted += new EventHandler<ServiceReference.updateUserCompletedEventArgs>(serviceReference_updateUserCompleted);
serviceReference.updateUserAsync();
ManualResetEvent m = new ManualResetEvent(true);
while !finished
Thread.Sleep(100);
doStuffWithResult(result);
}
void serviceReference_updateUserCompleted(object sender, ServiceReference.updateUserCompletedEventArgs e)
{
result = e.Result;
finished = true;
}
Related
Here is my code for refrence
public class ProgressChangedObject
{
public string Asin { get; set; }
public string Title { get; set; }
public DownloadProgress downloadProgress { get; set; }
public ProgressChangedObject(string asin, string title, DownloadProgress progress)
{
Asin = asin;
Title = title;
downloadProgress = progress;
}
}
public record QueueFile(string asin, string name);
public class BlockingCollectionQueue : IDownloadQueue
{
private BlockingCollection<QueueFile> _jobs = new BlockingCollection<QueueFile>();
public volatile List<QueueFile> queueFiles = new List<QueueFile>();
private readonly AudibleApi.Api _api;
private readonly LibraryPath _libraryPath;
private volatile float percentComplete;
private volatile QueueFile? currentJob;
private System.Timers.Timer _timer;
public BlockingCollectionQueue(AudibleApi.Api api, LibraryPath libraryPath)
{
var thread = new Thread(new ThreadStart(OnStart));
thread.IsBackground = true;
thread.Start();
_api = api;
_libraryPath = libraryPath;
_timer = new System.Timers.Timer(500);
_timer.Elapsed += TimerTick;
}
~BlockingCollectionQueue()
{
_timer.Dispose();
}
private void TimerTick(object? sender, System.Timers.ElapsedEventArgs e)
{
if (currentJob != null)
{
ProgressChanged?.Invoke(new ProgressChangedObject(currentJob.asin, currentJob.name, new DownloadProgress() { ProgressPercentage = percentComplete }));
}
}
public event Action<ProgressChangedObject>? ProgressChanged;
public void Enqueue(QueueFile job)
{
_jobs.Add(job);
queueFiles.Add(job);
}
private async void OnStart()
{
foreach (var job in _jobs.GetConsumingEnumerable(CancellationToken.None))
{
_timer.Enabled = true;
var d = new Progress<DownloadProgress>();
EventHandler<DownloadProgress> del = (object? sender, DownloadProgress e) => { if (e.ProgressPercentage is not null) { percentComplete = (float)e.ProgressPercentage; currentJob = job; } };
d.ProgressChanged += del;
await _api.DownloadAsync(job.asin, _libraryPath, new AsinTitlePair(job.asin, job.name), d);
d.ProgressChanged -= del;
_timer.Enabled = false;
queueFiles.Remove(job);
}
}
public List<QueueFile> GetQueue()
{
//MessageBox.Show(_jobs.GetConsumingEnumerable().Count().ToString());
return queueFiles;
}
}
I have an instance of this class in my app and when I need to download something I simply add a new queuefile to the queue, the problem is when I subscribe to the Progress changed event like this :
IDownloadQueue downloadQueue = new BlockingCollectionQueue();
downloadQueue.ProgressChanged += OnQueueChanged;
private void OnQueueChanged(ProgressChangedObject obj)
{
\\ some textblock in my xaml
myTextBlock.Text = obj.Title;
}
It throws this error:
Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll
An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code
The calling thread cannot access this object because a different thread owns it.
How can I fix this?
You should use Dispatcher.Invoke Method
You cannot access the GUI thread from a background thread. However, you can use as per below:
Dispatcher.BeginInvoke(new Action(() =>
{
// FooProgressBar.Value = ProgressData();
}));
For more information visit Threading Model
Question 1: I want to refresh a label by a work thread by delegate and invoke. It works well until i try to close the form. In closing event, I stop the work thread and then the UI thread while an exception occurs(Object disposed exception). It seems that form1 is disposed. I don't know what's wrong.
Question 2: When running this code, the memory usage keep increasing. I don't think it should take so much memory space. You can see find this by checking the task manager.
here's my code:
(.Net Framework 4, winforms)
Scheduler:
class Scheduler
{
private Thread[] workThreads = null;
//Scheduler started flag
public static bool bSchedulerStarted = false;
//Work threads stop event
public static EventWaitHandle stopWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
private static Scheduler self = null;
public static Scheduler getInstance()
{
if (self == null)
{
self = new Scheduler();
}
return self;
}
private Scheduler()
{
workThreads = new Thread[1];
}
private void CreateThread()
{
workThreads[0] = new Thread(Worker.doWork);
workThreads[0].IsBackground = true;
}
public void startUp()
{
if (!bSchedulerStarted)
{
stopWaitHandle.Reset();
CreateThread();
//Start all work threads
for (int i = 0; i < 1; i++)
{
workThreads[i].Start();
}
bSchedulerStarted = true;
}
}
public void stop()
{
if (!bSchedulerStarted)
return;
//Send stop event
stopWaitHandle.Set();
bSchedulerStarted = false;
if (workThreads != null)
{
//wait for all work threads to stop
for (int i = 0; i <1; i++)
{
if (workThreads[i] != null && workThreads[i].IsAlive)
workThreads[i].Join();
}
}
}
}
Worker:
class Worker
{
public static void doWork()
{
while (true)
{
if (Scheduler.stopWaitHandle.WaitOne(10, false) == true)
{
break;
}
Form1.count++;
Form1.sysMsgEvent.Set();
Thread.Sleep(10);
}
}
}
And form:
public partial class Form1 : Form
{
public static int count = 0;
public static EventWaitHandle sysMsgEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
public static EventWaitHandle stopEvent = new EventWaitHandle(false, EventResetMode.ManualReset);
private EventWaitHandle[] waitEvent = null;
Thread UIThread = null;
private delegate void ShowMsg();
public Form1()
{
InitializeComponent();
waitEvent = new EventWaitHandle[2];
waitEvent[0] = stopEvent;
waitEvent[1] = sysMsgEvent;
}
public void UpdateUI()
{
while (true)
{
switch (EventWaitHandle.WaitAny(waitEvent))
{
case 0: //Stop UI thread
return;
case 1: //Refresh UI elements
updateLabel();
break;
default:
return;
}//switch
}//while
}
private void updateLabel()
{
if (label1.InvokeRequired)
{
ShowMsg d = new ShowMsg(updateLabel);
this.Invoke(d, null);
}
else
{
label1.Text = count.ToString();
}
}
private void Form1_Load(object sender, EventArgs e)
{
UIThread = new Thread(new ThreadStart(UpdateUI));
UIThread.Start();
Scheduler sc = Scheduler.getInstance();
sc.startUp();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Scheduler sc = Scheduler.getInstance();
sc.stop();
//stop UI thread
Form1.stopEvent.Set();
}
}
This is my class GeckoBrowserForm
namespace NCrawler.GeckoProcessor
{
public partial class GeckoBrowserForm : Form
{
#region Readonly & Static Fields
private static bool s_IsXulrunnerInitialized;
private readonly GeckoWebBrowser m_GeckoWebBrowser = new GeckoWebBrowser();
private readonly string m_Url;
private SynchronizationContext m_uiContext;
#endregion
#region Constructors
static GeckoBrowserForm()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
}
public GeckoBrowserForm(string xulRunnerPath, string url)
{
m_uiContext = SynchronizationContext.Current;
if (m_uiContext == null)
{
m_uiContext = new SynchronizationContext();
}
InitializeXulRunner(xulRunnerPath);
m_Url = url;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
Location = new Point(0, 0);
Size = new Size(800, 800);
Done = false;
InitializeComponent();
}
#endregion
#region Instance Properties
public string DocumentDomHtml { get; set; }
public Boolean Done { get; set; }
#endregion
#region Instance Methods
protected override void OnLoad(EventArgs e)
{
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
private void DoWork()
{
// lets see the thread id
int id = Thread.CurrentThread.ManagedThreadId;
Trace.WriteLine("Run thread: " + id);
m_uiContext.Send(new SendOrPostCallback(_ =>
{
m_GeckoWebBrowser.Parent = this;
m_GeckoWebBrowser.Dock = DockStyle.Fill;
m_GeckoWebBrowser.DocumentCompleted += (s, ee) =>
{
GeckoHtmlElement element = null;
var geckoDomElement = m_GeckoWebBrowser.Document.DocumentElement;
if (geckoDomElement != null && geckoDomElement is GeckoHtmlElement)
{
element = (GeckoHtmlElement)geckoDomElement;
DocumentDomHtml = element.InnerHtml;
}
};
m_GeckoWebBrowser.Navigate(m_Url);
}), null);
}
#endregion
#region Class Methods
private static void InitializeXulRunner(string path)
{
if (s_IsXulrunnerInitialized)
{
return;
}
s_IsXulrunnerInitialized = true;
Xpcom.Initialize(path);
}
#endregion
}
}
but DoWork method get an excpetion
GeckoFx can only be called from the same thread on which it was
initialized (normally the UI thread)
I have read that I need to use SynchronizationContext but it is the same. Still the same error. I am ceating a form in some other project with
GeckoBrowserForm geckoBrowserForm = new GeckoBrowserForm(XulRunnerPath, Uri.ToString()))
geckoBrowserForm.Show();
and it is inside thread so I want to have 5 browsers.
What can I do? Is SynchronizationContext correct implemented?
have such code.
Start threads:
Thread[] thr;
static object locker = new object();
bool liking = true;
private void button2_Click(object sender, EventArgs e)
{
button2.Enabled = false;
button3.Enabled = true;
string post = create_note();
decimal value = Program.Data.numericUpDown1;
int i = 0;
int j = (int)(value);
thr = new Thread[j];
for (; i < j; i++)
{
thr[i] = new Thread(() => invite(post));
thr[i].IsBackground = true;
thr[i].Start();
}
}
public void invite(string post)
{
while (liking)
{
if (//some comdition)
exit all threads, and start string post = create_note(); again
}
}
If some condition in invite(string post) comes true I need to stop all threads, and go to string post = create_note(); again, get string post and start threads again.
How to do it?
Instead of manual thread management, use Parallel.For with CancellationToken:
var cts = new CancellationTokenSource();
var options = new ParallelOptions
{
CancellationToken = cts.Token,
MaxDegreeOfParallelism = System.Environment.ProcessorCount
};
var result = Parallel.For(0, j, options, i =>
{
invite(post);
options.CancellationToken.ThrowIfCancellationRequested();
});
When you want to cancel parallel calculations, just call cts.Cancel() from external code.
You can use lock and create a class that manage your threads like that :
public class SyncClass
{
public Thread[] thr;
private int NumberOfWorkingThreads { get; set; }
private object Sync = new object();
public int ThreadNumber { get; private set; }
public event EventHandler TasksFinished;
public SyncClass(int threadNumber)
{
thr = new Thread[threadNumber];
ThreadNumber = threadNumber;
NumberOfWorkingThreads = ThreadNumber;
//LunchThreads(threadNumber);
}
protected void OnTasksFinished()
{
if (TasksFinished == null)
return;
lock (Sync)
{
NumberOfWorkingThreads--;
if (NumberOfWorkingThreads == 0)
TasksFinished(this, new EventArgs());
}
}
public void LunchThreads()
{
string post = create_note();
for (int i = 0; i < ThreadNumber; i++)
{
thr[i] = new Thread(() => invite(post));
thr[i].IsBackground = true;
thr[i].Start();
}
}
private void invite(string post)
{
while (true)
{
if (true)
{
break;
}
}
OnTasksFinished();
}
}
Use the event to notify the end of all threads then the class will be used like that:
private void Operation()
{
var sync = new SyncClass(10);
sync.TasksFinished += sync_TasksFinished;
sync.LunchThreads();
}
void sync_TasksFinished(object sender, EventArgs e)
{
Operation();
}
Each time CurlFile() is called it creates a new object from the ProcessObject class. The ProcessObject object starts the new Process. I want the Process.Exit event in each object to trigger the static event handler in the parent class, but it doesn't seem to be working for some reason.
class Curl
{
StringContainer oStrings = new StringContainer();
private static int _counter = 0;
private string _curl;
public Curl()
{
//oStartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
_curl = oStrings.Curl;
}
public void CurlFile(string _arg)
{
ProcessObject oProcessObject = new ProcessObject(_arg, _curl);
}
private static void oProcess_Exited(object sender, EventArgs e)
{
_counter++;
if (_counter == 1000)
{
MessageBox.Show("here");
}
}
private class ProcessObject
{
ProcessStartInfo oStartInfo = new ProcessStartInfo();
Process oProcess = new Process();
public ProcessObject(string _arg, string _curl)
{
oStartInfo.FileName = _curl;
oStartInfo.Arguments = _arg;
oProcess.EnableRaisingEvents = true;
oProcess.Exited += new EventHandler(oProcess_Exited);
oProcess = Process.Start(oStartInfo);
}
}
}
First off, as #Will mentions, you need to keep a reference to your Process objects so they don't get GC'd. Something like this (code untested):
class Curl
{
internal static List<ProcessObject> _processes = new List<ProcessObject>();
// ....
private static void oProcess_Exited(object sender, EventArgs e)
{
var p = sender as Process;
if (p != null && _processes.Contains(p))
_processes.Remove(p);
_counter++;
if (_counter == 1000)
{
MessageBox.Show("here");
}
}
public ProcessObject(string _arg, string _curl)
{
oStartInfo.FileName = _curl;
oStartInfo.Arguments = _arg;
oStartInfo.UseShellExecute = false;
oProcess.EnableRaisingEvents = true;
oProcess.Exited += new EventHandler(oProcess_Exited);
oProcess = Process.Start(oStartInfo);
Curl._processes.Add(oProcess);
}
}
Also, as some people have found, the Process class can be spotty about detecting the Exit. I don't know if this holds true for the Exited event also, but I'd watch out for it and explicitly set UseShellExecute = false; as I have above.