Simple as this! This is my attempt at one, which requires that functions to be threaded with it use a Pause() function through itself in pausable sections.
using System;
using System.Threading;
class BlackThread {
private bool paused;
private Thread innerThr;
// ---
public bool IsAlive {
get {
return innerThr.IsAlive;
}
}
// ===
public void SetAndGo (ThreadStart start) {
paused = false;
innerThr = new Thread(start);
innerThr.Start();
WaitForIt();
}
// ---
public void Pause() {
paused = true;
while (paused);
}
public void Unpause() {
paused = false;
}
public void WaitForIt() {
while(!paused && IsAlive);
}
public void Continue() {
Unpause();
WaitForIt();
}
}
class MainClass {
static void pausableFunction (BlackThread self) {
Console.WriteLine("* Waiting...");
self.Pause();
Console.WriteLine("* Doing stuff.");
self.Pause();
Console.WriteLine("* Finished!");
}
static void Main() {
BlackThread noir = new BlackThread();
noir.SetAndGo(() => pausableFunction(noir));
while (noir.IsAlive) {
Console.Write("> ");
Console.ReadKey();
noir.Continue();
}
}
}
Sadly, it's not one that can be paused at any time, but a thread for functions that require to wait for outside processing to be able to continue. Like an action by a game mob that requires its frame to be drawn by the draw loop before it can continue, and the mob's A.I.'s is processed in the game's main loop.
I guess it'd make it some kind of pseudo-thread? Anyway.
It would allow the mob to process this action bit by bit every loop, instead of cascading checkings in its A.I. like...
if mob is doing action {
if mob has already done this previous part of the action {
do the following part
}
}
...it'd rather be like this, in a thread:
do the first step of the action
Wait for it to be rendered...
do the following step of the action
Wait for it to be rendered...
do the last step of the action
(Action ends here, no need to wait for anything anymore)
Now, my implementation has a bug which I cannot figure out how to fix. When it's supposed to unpause the BlackThread, it remains paused in the function (in this case, pausableFunction()) that uses it. I guess it's because of how the instance is passed?
If it's what I'm guessing - that is, something (and I'd guess it's bool paused) is passed by value instead of reference - how could I fix it?
I'm really used to the pointers of C and C++, so sometimes I get a bit tangled when dealing in C# with the communication of an object's values between scopes.
This here is a version of the code the works, a prototype to say:
using System;
using System.Threading;
class Program {
static bool paused;
static void Pause() {
paused = true;
while (paused);
}
static void Unpause() {
paused = false;
}
static void WaitForIt(Thread waited) {
while(!paused && waited.IsAlive);
}
static void Continue (Thread ToStop) {
Unpause();
WaitForIt(ToStop);
}
static void SetAndGo (out Thread thread, ThreadStart Start) {
thread = new Thread(Start);
thread.Start();
WaitForIt(thread);
}
// ---
static void thr (string chant) {
// Console.WriteLine("Waiting...");
// Pause();
// Console.WriteLine("{0}", chant);
// Pause();
// Console.WriteLine("Well, I'm finished!");
Console.WriteLine("I'm finished!");
}
static void Main() {
// Thread tt = new Thread(() => thr());
// tt.Start();
// WaitForIt(tt);
Thread tt;
SetAndGo(out tt, (() => thr("I'm doing stuff.")));
while (tt.IsAlive) {
Console.Write("> ");
Console.ReadKey();
Continue(tt);
}
}
}
I'm only not using it because I'd rather have everything in charge of a specific class for the matter, something that would also enhance readability.
Alright, I've accomplished what I was trying already, so I'll leave my code here for future reference!
This is the BlackThread class in the end:
using System;
using System.Threading;
class BlackThread {
//* ===== *//
private AutoResetEvent pauser = new AutoResetEvent(false);
private AutoResetEvent waiter = new AutoResetEvent(false);
private Thread innerThr;
// ----- //
public bool IsActing {
get {
if (innerThr != null) return innerThr.IsAlive;
else return false;
}
}
//* ===== *//
public void KickStart_(ThreadStart start) {
innerThr = new Thread(start);
innerThr.Start();
WaitForIt();
}
// ----- //
// FOR THE THREADED FUNCTION
public void Wait() {
waiter.Set();
pauser.WaitOne();
}
public void End() {
waiter.Set();
}
// ----- //
// FOR BLACKTHREAD MANAGING
private void WaitForIt() {
waiter.WaitOne();
}
public void Continue() {
if (IsActing) {
pauser.Set();
WaitForIt();
}
}
//* ===== *//
}
And here, an example of its use:
class MainClass {
static void pausableFunction() {
Console.WriteLine("* Waiting...");
Event.Wait();
Console.WriteLine("* Doing stuff.");
Thread.Sleep(1000);
Event.Wait();
Console.WriteLine("* Finished!");
Event.End();
}
static void anotherFunction(int foo) {
Console.WriteLine("* Wanna know the value of a number?");
Event.Wait();
Console.WriteLine("* I'll tell you. It's {0}!", foo);
Event.End();
}
static void simpleFunction() {
Console.WriteLine("* I'm done already!");
}
static BlackThread Event = new BlackThread();
static Random Rand = new Random();
static void Main() {
int r;
do {
if (!Event.IsActing) {
Console.WriteLine();
r = Rand.Next(3);
if (r == 0) {
Event.KickStart_(() => pausableFunction());
}
else if (r == 1) {
simpleFunction();
}
else {
Event.KickStart_(() => anotherFunction(Rand.Next(20) + 1));
}
}
else {
Event.Continue();
}
Console.Write("> ");
Console.ReadKey();
} while(true);
}
}
What I've opted to use in the end were two AutoResetEvent handlers. One is managed in the function of the thread that requires pausing, and that pauses the main loop, the waiter ARE, and another, the pauser ARE, managed in the main loop, and that pauses the thread with the function with support for BlackThread; that is, has acess to a BlackThread instance.
In this case I've used a static BlackThread object, but it can also be passed as a parameter to the function.
And yes, it's named after the Buddhist hell!
Related
I am having a lot of trouble with this. Consider this example:
public class Test {
Thread t;
public Test() {
t = new Thread(ThreadFunction);
}
public void Start() {
t.Start();
}
private void ThreadFunction() {
Thread.Sleep(5000);
Console.WriteLine("Function Complete");
}
}
public static class Main {
public Main() {
Test test = new Test();
test.Start();
// sleep longer than my worker so it finishes
Thread.Sleep(10000);
// a place to place a breakpoint
bool breakPointHere = true;
}
}
Now, I see the output of the console.log, but when I inspect Test's thread object, I see that IsAlive is still true, and ThreadStatus = TheadStatus.Running. Why is this? I wish to detect that the thread is truly complete, but I am confused as to how it can still be considered running if ThreadFunction() completes?
EDIT 2:
I finally tracked down the cause, Updating the code, and am going to answer my own question
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
public abstract class Worker {
protected bool shutdown;
protected Thread t;
private bool _isStopped = true;
public bool IsStopped {
get {
return t.ThreadState == ThreadState.Stopped;
}
}
private bool _isPaused = false;
public bool IsPaused {
get {
return _isPaused;
}
}
private string stringRepresentation;
public Worker() {
t = new Thread(ThreadFunction);
stringRepresentation = "Thread id:" + t.ManagedThreadId;
t.Name = stringRepresentation;
}
public void Start() {
OnBeforeThreadStart();
t.Start();
}
public void ScheduleStop() {
shutdown = true;
}
public void SchedulePause() {
OnPauseRequest();
_isPaused = true;
}
public void Unpause() {
_isPaused = false;
}
public void ForceStop() {
t.Abort();
}
/// <summary>
/// The main thread loop.
/// </summary>
private void ThreadFunction() {
OnThreadStart();
while (!shutdown) {
if (!IsPaused) {
if (!OnLoop()) {
break;
}
}
Thread.Sleep(1000);
}
OnShutdown();
}
public abstract void OnBeforeThreadStart();
public abstract void OnThreadStart();
public abstract bool OnLoop();
public abstract void OnShutdown();
public abstract void OnPauseRequest();
public override string ToString() {
return stringRepresentation;
}
}
public class Test : Worker {
public override void OnBeforeThreadStart() {
Log.WriteLine(this + ": Thread about to be started...");
}
public override void OnThreadStart() {
Log.WriteLine(this + ": Thread Started!");
}
public override bool OnLoop() {
Log.WriteLine(this + ": I am doing the things...");
return true;
}
public override void OnShutdown() {
Log.WriteLine(this + ": Shutting down!");
}
public override void OnPauseRequest() {
}
}
public static class Log {
public delegate void LogDelegate(string text, string eventTime, Severity severity);
public static event LogDelegate OnWriteLine;
private static Queue<string> _pendingFileWrites = new Queue<string>();
public enum Severity {
Info,
Warning,
Error
}
public static void WriteLine(object line, Severity severity = Severity.Info) {
string eventTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string formatted = "[" + eventTime + "]: " + line;
Console.WriteLine(formatted);
lock (_pendingFileWrites) {
_pendingFileWrites.Enqueue(formatted);
}
if (OnWriteLine != null) {
// this is the offending line:
OnWriteLine.Invoke((string)line, eventTime, severity);
}
}
public static void WriteToFile(string path) {
lock(_pendingFileWrites) {
StreamWriter sw = File.AppendText(path);
while(_pendingFileWrites.Count > 0) {
sw.WriteLine(
_pendingFileWrites.Dequeue()
);
}
sw.Close();
}
}
}
class Program {
static void Main(string[] args) {
List<Test> tests = new List<Test>();
for(int i = 0; i < 10; i++) {
Test test = new Test();
test.Start();
tests.Add(test);
}
// sleep a little bit so they do the things
Thread.Sleep(10000);
foreach (Test test in tests) {
test.ScheduleStop();
}
bool allStopped;
do {
allStopped = true;
foreach (Test test in tests) {
if (!test.IsStopped) {
allStopped = false;
break;
}
}
} while (!allStopped);
Console.WriteLine("Done!");
// a place to place a breakpoint
bool breakPointHere = true;
}
}
}
I think your original testing that lead you to believe .IsAlive would be true had some flaw in it, I tweaked your program in your question to the following to make it compile and to be able to see which thread it created.
public class Program
{
public class Test
{
Thread t;
public Test()
{
t = new Thread(ThreadFunction);
t.Name = "TestThread";
}
public void Start()
{
t.Start();
}
private void ThreadFunction()
{
Thread.Sleep(5000);
Console.WriteLine("Function Complete");
}
}
public static void Main()
{
Test test = new Test();
test.Start();
// sleep longer than my worker so it finishes
Thread.Sleep(10000);
// a place to place a breakpoint
bool breakPointHere = true;
}
}
here is a screenshot of the running threads from inside ThreadFunction
Here is a screenshot from the end of the program
Notice that there is no "TestThread" thread.
Here is a screenshot from the locals window
IsAlive is false.
Do you really need to sleep to wait for your thread to finish?
If you don't, a better and more robust solution would be using Thread.Join()
public static class Main {
public Main() {
Test test = new Test();
test.Start();
test.Join(); // Waits for test to complete
bool breakPointHere = true;
}
}
So it turns out that my issue was that my logging method was calling a UI thread function like so:
private void LogToForm(object line, string eventTime, Log.Severity severity) {
if (dataGridView_LogInfo.InvokeRequired) {
dataGridView_LogInfo.Invoke (
new Action<object, string, Log.Severity>(LogtoFormCallback),
new object[] { line, eventTime, severity }
);
} else {
LogtoFormCallback(line, eventTime, severity);
}
}
At the Invoke() line, the thread would hang forever. The solution was to replace it with BeginInvoke() instead.
EDIT: Also, my example was/is quite poor for this. I thought I didn't understand threads at a fundamental level, and that my examples would have been enough. Hopefully someone googles this though and has this same cause, and can try this solution.
When using the StartNew() method to kick off a process on a new thread, I need to figure out how to make another call into this object in that same thread (I assume this would be some sort of Join operation?).
The following example is dumbed down to illustrate the meat of what I am trying to do. I am well aware it is severely lacking in basic concurrency considerations. But I didn't want to cloud the code with all of that logic, so please forgive me on that.
The following console app shows what I am trying to accomplish. Assume on the StartNew() call a new thread with ID 9976 is created and the method invoked there. I would like the subsequent call to ProcessImmediate() in the file system watcher change event handler to be made on thread 9976 as well. As it stands, the call would share the same thread that is used for the file system watcher change event.
Can this be done, and if so, how?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
public void Run()
{
_activity = new Activity();
// start activity on a new thread
Task.Factory.StartNew(() => _activity.Go());
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "c:\temp";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity
{
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
System.Threading.Thread.Sleep(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
DoSomethingInteresting();
}
public bool Stop { get; set; }
}
}
* UPDATE *
Thanks for the excellent responses. I took Mike's suggestion and implemented it for my console app. Below is the full working code which also includes the use of a cancellation token. I post this in case someone else might find it useful.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
runner.Stop();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
private CancellationTokenSource _cts = new CancellationTokenSource();
public void Stop() { _cts.Cancel(); }
public void Run()
{
_activity = new Activity();
// start activity on a new thread
var task = new Task(() => _activity.Go(_cts.Token), _cts.Token, TaskCreationOptions.LongRunning);
task.Start();
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "C:\\Temp\\FileSystemWatcherPath";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go(CancellationToken ct)
{
Thread.CurrentThread.Name = "Go";
while (!ct.IsCancellationRequested)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(5000);
}
Console.WriteLine("Exiting");
}
protected virtual void DoSomethingInteresting()
{
Console.WriteLine(string.Format("Doing Something Interesting on thread {0}", Thread.CurrentThread.ManagedThreadId));
}
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
_processing.Set();
}
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
}
First, you should use TaskCreationOptions.LongRunning if you are creating a task that will not complete quickly. Second, use an AutoResetEvent to signal the waiting thread to wake up. Note that below ProcessImmediate will return before DoSomethingInteresting has completed running on the other thread. Example:
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
User mike has given a better solution, which will be appropriate when you like to call the same method immediately. If you want to call a different methods immediately I'll expand mike's answer to achieve that.
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
private ConcurrentQueue<Action> actionsToProcess = new ConcurrentQueue<Action>();
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
while(!actionsToProcess.IsEmpty)
{
Action action;
if(actionsToProcess.TryDeque(out action))
action();
}
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate(Action action)
{
actionsToProcess.Enqueue(action);
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
To execute different methods on the same thread you can use a message loop that dispatches incoming requests. A simple option would be to use the event loop scheduler of the Reactive Extensions and to "recursively" schedule your Go() function - if in the mean time a different operation is scheduled it would be processed before the next Go() operation.
Here is a sample:
class Loop
: IDisposable
{
IScheduler scheduler = new EventLoopScheduler();
MultipleAssignmentDisposable stopper = new MultipleAssignmentDisposable();
public Loop()
{
Next();
}
void Next()
{
if (!stopper.IsDisposed)
stopper.Disposable = scheduler.Schedule(Handler);
}
void Handler()
{
Thread.Sleep(1000);
Console.WriteLine("Handler: {0}", Thread.CurrentThread.ManagedThreadId);
Next();
}
public void Notify()
{
scheduler.Schedule(() =>
{
Console.WriteLine("Notify: {0}", Thread.CurrentThread.ManagedThreadId);
});
}
public void Dispose()
{
stopper.Dispose();
}
}
static void Main(string[] args)
{
using (var l = new Loop())
{
Console.WriteLine("Press 'q' to quit.");
while (Console.ReadKey().Key != ConsoleKey.Q)
l.Notify();
}
}
I need to call a 3rd party code that optionally starts a new thread, performs some processing, and then calls a different method on my object. What I need is wait for the 3rd party processing to be finished, then return from the original method. In other words, I have a class like this (C#):
class MyClass: IThirdPartyInterface {
void MyMethod() {
//some preprocessing
//call a 3rd party static method
ThirdParty.DoSomething(this);
}
void FinishedProcessing() {
//some postprocessing
//???
}
}
I want to modify MyMethod so that it return only after the thread that started in DoSomething has finished its execution and called the FinishedProcessing method. Since the thread is started by the third party code, I don't have access to it, so I cannot use Thread.Join here. So, what do I do instead?
You need to use an System.Threading.AutoResetEvent, it would be like this:
class MyClass: IThirdPartyInterface {
AutoResetEvent _event = new AutoResetEvent(false);
void MyMethod() {
ThirdParty.DoSomething(this);
_event.WaitOne();
}
void FinishedProcessing() {
_event.Set();
}
}
If the thread continues running after your FinishedProcessing method is called by the 3rdparty class, it would be a little diferent:
class MyClass: IThirdPartyInterface {
AutoResetEvent _event = new AutoResetEvent(false);
Thread _thread;
void MyMethod() {
ThirdParty.DoSomething(this);
_event.WaitOne();
_thread.Join();
}
void FinishedProcessing() {
_thread = Thread.CurrentThread;
_event.Set();
}
}
Make your MyMethod() async and then run thirdparty method inside your custom await method, moething like this:
private async void MyMethod()
{
var result = await WaitAsynchronouslyAsync();
}
public async Task<string> WaitAsynchronouslyAsync()
{
await ThirdParty.DoSomething(this);
return "Finished";
}
If ThirdParty.DoSomething does not support async pattern
you can use additional proxy with finalizer.
But it could affect application performance like a "while(myBoolFlag){}".
class Program
{
static void Main(string[] args)
{
var list = new List<ManualResetEvent>();
for (var i = 0; i < 10000; i++)
{
var m = new ManualResetEvent(false);
list.Add(m);
new Thread(Start).Start(m);
if (i > 0 && (i % 10) == 0)
for (int j = i - 10; j < i; j++)
{
list[j].WaitOne(1000);// wait signal
GC.Collect(); //force finalizer
A.Print();
}
}
}
private static void Start(object obj)
{
new A(obj as ManualResetEvent, null);
}
}
public class A : IThirdPartyInterface
{
public static long time1;
public static long count1;
private DateTime start = DateTime.Now;
private ManualResetEvent _stop;
private IThirdPartyInterface _origin;
public A(ManualResetEvent stop, IThirdPartyInterface origin)
{
_stop = stop;
_origin = origin;
}
~A()
{
Interlocked.Increment(ref count1);
Interlocked.Add(ref time1, (long)(DateTime.Now - start).TotalMilliseconds);
_stop.Set(); //send signal
}
public static void Print()
{
Console.Write("\r" + A.time1 + "\\" + A.count1 + " ");
if (A.count1 != 0)
Console.Write((A.time1 / A.count1).ToString());
}
}
All the examples I have seen using SynchronisationContext.Post have been used in the same class. What I have is the UI thread passing some by-ref arguments to a threadwrapper class, updating the arguments and then I want it to update some labels etc on the UIThread.
internal class ConnThreadWrapper
{
....
public event EventHandler<MyEventArgs<String, Boolean>> updateConnStatus =
delegate { };
public void updateUIThread(string conn, bool connected)
{
uiContext.Post(new SendOrPostCallback((o) =>
{
updateConnStatus(this,
new MyEventArgs<String, Boolean>(conn,
connected));
}),
null);
}
}
//on ui thread
public void updateConnStatus(object sender, MyEventArgs<String, Boolean> e)
{
switch (e.val1)
{
case "CADS" :
if (e.val2 == true)
{
}
The Event seems to fire without any errors but nothing is ever received on the uiThread - i'm not sure if my signature for the sub updateConnStatus is correct or if it works like this. I obviously want the event to handled on the uithread and update the labels from that sub.
In a previous vb.net project I used to reference the form directly on the thread and used a delegate to invoke a callback but apparently this was a bad design as I was mixing application layers. I wanted to use the sync context as it was meant to be thread safe but most of the examples i've seen have used invoke.
Any ideas what I'm missing? Thanks
I wrote this helper class which works for me. Prior to using this class call InitializeUiContext() on UI thread somewhere on application start.
public static class UiScheduler
{
private static TaskScheduler _scheduler;
private static readonly ConcurrentQueue<Action> OldActions =
new ConcurrentQueue<Action>();
public static void InitializeUiContext()
{
_scheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private static void ExecuteOld()
{
if(_scheduler != null)
{
while(OldActions.Count > 0)
{
Action a;
if(OldActions.TryDequeue(out a))
{
UiExecute(_scheduler, a);
}
}
}
}
private static void UiExecute(TaskScheduler scheduler,
Action a,
bool wait = false)
{
//1 is usually UI thread, dunno how to check this better:
if (Thread.CurrentThread.ManagedThreadId == 1)
{
a();
}
else
{
Task t = Task.Factory.StartNew(a,
CancellationToken.None,
TaskCreationOptions.LongRunning,
scheduler);
if (wait) t.Wait();
}
}
public static void UiExecute(Action a, bool wait = false)
{
if (a != null)
{
if (_scheduler != null)
{
ExecuteOld();
UiExecute(_scheduler, a, wait);
}
else
{
OldActions.Enqueue(a);
}
}
}
}
In the end I ditched the ThreadWrapper and trying to marshal the event to the UI Thread and used a Task instead, in fact I think I can use task to do most of the stuff in this project so happy days.
Task<bool> t1 = new Task<bool>(() => testBB(ref _bbws_wrapper));
t1.Start();
Task cwt1 = t1.ContinueWith(task => { if (t1.Result == true) { this.ssi_bb_conn.BackColor = Color.Green;} else { this.ssi_bb_conn.BackColor = Color.Red; } }, TaskScheduler.FromCurrentSynchronizationContext());
.....
private static bool testBB(ref BBWebserviceWrapper _bbwsw)
{
try
{
//test the connections
if (_bbwsw.initialize_v1() == true)
{
if (_bbwsw.loginUser("XXXXXXXX", "XXXXXXXXX") == true)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
catch
{
return false;
}
}
I have an object in C# on which I need to execute a method on a regular basis. I would like this method to be executed only when other people are using my object, as soon as people stop using my object I would like this background operation to stop.
So here is a simple example is this (which is broken):
class Fish
{
public Fish()
{
Thread t = new Thread(new ThreadStart(BackgroundWork));
t.IsBackground = true;
t.Start();
}
public void BackgroundWork()
{
while(true)
{
this.Swim();
Thread.Sleep(1000);
}
}
public void Swim()
{
Console.WriteLine("The fish is Swimming");
}
}
The problem is that if I new a Fish object anywhere, it never gets garbage collected, cause there is a background thread referencing it. Here is an illustrated version of broken code.
public void DoStuff()
{
Fish f = new Fish();
}
// after existing from this method my Fish object keeps on swimming.
I know that the Fish object should be disposable and I should clean up the thread on dispose, but I have no control over my callers and can not ensure dispose is called.
How do I work around this problem and ensure the background threads are automatically disposed even if Dispose is not called explicitly?
Here is my proposed solution to this problem:
class Fish : IDisposable
{
class Swimmer
{
Thread t;
WeakReference fishRef;
public ManualResetEvent terminate = new ManualResetEvent(false);
public Swimmer(Fish3 fish)
{
this.fishRef = new WeakReference(fish);
t = new Thread(new ThreadStart(BackgroundWork));
t.IsBackground = true;
t.Start();
}
public void BackgroundWork()
{
bool done = false;
while(!done)
{
done = Swim();
if (!done)
{
done = terminate.WaitOne(1000, false);
}
}
}
// this is pulled out into a helper method to ensure
// the Fish object is referenced for the minimal amount of time
private bool Swim()
{
bool done;
Fish fish = Fish;
if (fish != null)
{
fish.Swim();
done = false;
}
else
{
done = true;
}
return done;
}
public Fish Fish
{
get { return fishRef.Target as Fish3; }
}
}
Swimmer swimmer;
public Fish()
{
swimmer = new Swimmer(this);
}
public void Swim()
{
Console.WriteLine("The third fish is Swimming");
}
volatile bool disposed = false;
public void Dispose()
{
if (!disposed)
{
swimmer.terminate.Set();
disposed = true;
GC.SuppressFinalize(this);
}
}
~Fish()
{
if(!disposed)
{
Dispose();
}
}
}
I think the IDisposable solution is the correct one.
If the users of your class don't follow the guidelines for using classes that implement IDisposable it's their fault - and you can make sure that the documentation explicitly mentions how the class should be used.
Another, much messier, option would be a "KeepAlive" DateTime field that each method called by your client would update. The worker thread then checks the field periodically and exits if it hasn't been updated for a certain amount of time. When a method is setting the field the thread will be restarted if it has exited.
This is how I would do it:
class Fish3 : IDisposable
{
Thread t;
private ManualResetEvent terminate = new ManualResetEvent(false);
private volatile int disposed = 0;
public Fish3()
{
t = new Thread(new ThreadStart(BackgroundWork));
t.IsBackground = true;
t.Start();
}
public void BackgroundWork()
{
while(!terminate.WaitOne(1000, false))
{
Swim();
}
}
public void Swim()
{
Console.WriteLine("The third fish is Swimming");
}
public void Dispose()
{
if(Interlocked.Exchange(ref disposed, 1) == 0)
{
terminate.Set();
t.Join();
GC.SuppressFinalize(this);
}
}
~Fish3()
{
if(Interlocked.Exchange(ref disposed, 1) == 0)
{
Dispose();
}
}
}