C# Call an object from another thread - c#

I'm trying to program a small game where there are a few barriers that you have to jump over, go under and so on. Now, for that i can move the barriers and jump at the same time, I opened a new thread. This is in the Form1.cs:
public partial class Form1 : Form
{
Worker workerObject;
Thread workerThread;
public Form1()
{
InitializeComponent();
workerObject = new Worker();
workerObject.Form1 = this;
workerObject.initialize(this);
workerThread = new Thread(workerObject.DoWork);
}
And:
public class Worker
{
private volatile bool _shouldStop = false;
public volatile Form Form1;
public void initialize(Form pForm)
{
Form1 = pForm;
}
public void DoWork()
{
while (!_shouldStop)
{
_shouldStop = true;
picture_barrier1.Left = picture_barrier1.Left - 1;
}
}
public void RequestStop()
{
_shouldStop = true;
}
}
But I get the following error:
Error 1 Cannot access a non-static member of outer type 'Game.Form1'
via nested type 'Game.Form1.Worker'
Then when I go into the Form1.Designer.cs and make the barriers static (By the way, the objects are volatile), I get 11 errors from the code above containing:
Error 1 Member 'Game.Form1.picture_barrier1' cannot be accessed with
an instance reference; qualify it with a type name instead
How do I call those objects correctly from another thread?

Related

Getting unhandled ArgumentException when starting a new thread for a method of an instanced class

I have class HBClient which contains connect() method.
public void connect()
{
//connect to TCP socket
}
This is a winform, and I create instances of my Form1 and HBClient as such.
//Program.CS
Form1 form = new Form1();
HBClient hbClient = new HBClient();
//Form1.CS
//Form1 Class
public HBClient hbClient;
public void GetClient(HBClient receivedHBClient)
{
hbClient = receivedHBClient;
}
//HBClient Class
public Form1 form;
public void GetForm(Form1 receivedForm)
{
form = receivedForm;
}
and link them together...
hbClient.GetForm(form);
form.GetClient(hbClient);
When my form tries to call hbClient.connect in a thread, I get an unhandled ArgumentException.
Thread thread = new Thread(hbClient.connect);
thread.IsBackground = true;
thread.Start();
"Delegate to an instance method cannot have null 'this'."

WPF - Threads and change variable

I have application with 1 simple thread:
public partial class MainWindow : Window
{
Thread historyThread = null;
Window historyWindow = null;
public MainWindow()
{
//...
}
#region EVENTS Magazyn
private void btn_K_FinalizujZakup_Click(object sender, RoutedEventArgs e)
{
if (dg_Klient.Items.Count > 0)
{
//Problem with 'Finalizuj' Method
new Historia.Wpis(MainWindowViewModel.klient).Finalizuj(viewModel.historiaWindow_ViewModel.historiaZakupow);
MainWindowViewModel.klient = new Klient.Klient();
dg_Klient.ItemsSource = MainWindowViewModel.klient;
}
}
#region History Thread
private void btn_K_HistoriaOpen_Click(object sender, RoutedEventArgs e)
{
//if(historyThread != null){
// historyThread.Abort();
// historyThread = null;
//}
historyThread = new Thread(new ThreadStart(History_ThreadStart));
historyThread.SetApartmentState(ApartmentState.STA);
historyThread.IsBackground = true;
historyThread.Start();
}
private void History_ThreadStart()
{
historyWindow = new HistoryWindow(viewModel.historiaWindow_ViewModel);
historyWindow.Show();
historyWindow.Activate();
System.Windows.Threading.Dispatcher.Run();
}
#endregion // History Thread
//...
}
and 'Wpis' Class look like:
public class Wpis
{
private DateTime date;
public DateTime Date // normal get/ set
private ObservableCollection<Produkt> listaZakupow;
public ObservableCollection<Produkt> ListaZakupow // normal get/set
public Wpis(ObservableCollection<Produkt> listaZakupow)
{
date = DateTime.Now;
this.listaZakupow = listaZakupow;
}
public void Finalizuj(Historia historia)
{
//NOT! - Thread Safe
// EXCEPTION!
// NotSupportedException
// This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
historia.Add(this);
}
private void DoDispatchedAction(Action action)
{
if (currentDispatcher.CheckAccess())
{
action.Invoke();
}
else
{
currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
}
}
}
And when I don't run thread (historyThread) I can normal do method 'Finalizuj' many times
but when I run Thread I can't add anything to list (can't run method - 'Finalizuj')
And VS show me exception about:
NonSupportedException was unhandled
This type of CollectionView does not support changes SourceCollection collection from a thread other than the Dispatcher.
I dont really know what i do wrong.
What i need to add to my project?
In short:
In main Thread - I have object_1 (typeof: ObservableCollection)
In second Thread - I want add anothrer object (typeof: Wpis : ObservableCollection) to object_1
but I get the abovementioned exception
The Exception is telling you exactly what you need to do. You cannot modify the UI from a different thread. I'm assuming that your Observable collection is bound somewhere in your XAML.
You will need to obtain a reference to your MainWindow class so you can access the Dispatcher thread.
public static MainWindow Current { get; private set; }
public MainWindow()
{
Current = this;
//...
}
Then use the Dispatcher thread to modify your collection:
MainWindow.Current.Dispatcher.Invoke((Action)(() =>
{
historia.Add(this);
}));

C# multithreaded throbber form

Working on a C# project which I would like to implement a "waiting" (throbber) indicator in a separate form. After much research and trial and error it appears as the suggested method of doing this is to load a form using a separate thread from the one from the current form/thread.
The reason I went with this method was because initially using the Show() method on the throbber form produced a transparent form. I cannot use ShowDialog because I need to run some code after the throbber is displayed, after which that completes I would like to close the throbber form.
Anyway .. after trying many different methods to load the throbber form in a separate thread I still get an error about trying to access it from a thread which is different from the one it was created in. Here is a skelton version of the project code that should shed some light on my issue:
the example I was working off of for multithreading was this popular link for creating your own spashscreen in a separate thread ... http://www.codeproject.com/Articles/5454/A-Pretty-Good-Splash-Screen-in-C
public class Main
{
public void CheckData()
{
try
{
ProgressBar pb = new ProgressBar();
pb.ShowProgressBar();
//do data checking here
pb.CloseForm()
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static Thread ms_oThread = null;
public bool shouldStop = false;
static ProgressBar ms_ProgBar = null;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
public void ShowForm()
{
ms_ProgBar = new ProgressBar();
Application.Run(ms_ProgBar);
}
public void CloseForm()
{
ms_ProgBar.Close();
}
public void ShowProgressBar()
{
// Make sure it is only launched once.
if (ms_ProgBar != null)
return;
ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_ProgBar == null || ms_ProgBar.IsHandleCreated == false)
{
System.Threading.Thread.Sleep(1000);
}
}
}
You are creating your ProgressBar twice. Once in your main function, and once in your new thread. You are also calling your CloseWindow method from your main function (and on the window that is never shown), rather than on your new thread window.
You only want to create ProgressBar and show it using your new thread. Make your static ProgressBar field public so you can call close on it directly from Main, but make sure to use Invoke to do it since it's not on that Window's GUI thread.
Also, ShowProgressBar should be static.
Here's a rewrite attempt:
public class Main
{
public void CheckData()
{
try
{
ProgressBar.ShowProgressBar();
//do data checking here
ProgressBar.CloseForm();
}
catch(Exception e)
{
}
}
}
public partial class ProgressBar : Form
{
static ProgressBar _progressBarInstance;
public ProgressBar()
{
InitializeComponent();
//DoWork();
}
static void ShowForm()
{
_progressBarInstance = new ProgressBar();
Application.Run(ms_ProgressBar);
}
public static void CloseForm()
{
_progressBarInstance.Invoke(new Action(_progressBarInstance.Close));
_progressBarInstance= null;
}
public static void ShowProgressBar()
{
// Make sure it is only launched once.
if (_progressBarInstance != null)
return;
var ms_oThread = new Thread(new ThreadStart(ShowForm));
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
}
}

Infinite loop between main program and thread

namespace MP3_speler.Threading
{
public class thread : MainWindow
{
public thread()
{
StartThreading();
}
public void StartThreading()
{
Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
thread.Priority = ThreadPriority.BelowNormal;
thread.Start();
}
public void WorkThreadFunction()
{
try
{
UpdateMyDelegatedelegate UpdateMyDelegate = new UpdateMyDelegatedelegate(UpdateMyDelegateLabel);
timelabel.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, UpdateMyDelegate, (Convert.ToInt32(mp3FileReader.Position / 10000)));
Thread.Sleep(500);
}
catch
{
}
}
private void UpdateMyDelegateLabel(int i)
{
double timeseconds = (double)mp3FileReader.Position / (double)176000;
TimeSpan t = TimeSpan.FromSeconds(timeseconds);
string answer = string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds
);
timelabel.Content = answer;
if (waveOut.PlaybackState != PlaybackState.Paused) Slider2.Value = mp3FileReader.Position;
}
}
}
Code Mainprogram:
public partial class MainWindow : Window
{
public IWavePlayer waveOut;
public Mp3FileReader mp3FileReader;
public delegate void UpdateMyDelegatedelegate(int i);
public MainWindow()
{
InitializeComponent();
//Create a thread
thread thread = new thread();
//Setting up notifyicon
notifyCenter notify = new notifyCenter(this);
//giving values to bars
Slider1.Value = 5;
volumelabel.Content = 50;
}
This code generates an infinite loop and goes back to Initializecomponent all the time.
Maybe the problem is that I made the Thread class inherit the MainWindow but would like to know what the problem is.
Yes, the fact that thread derives from MainWindow is causing the infinite loop.
In MainWindow's constructor, it creates a new thread object. However, because thread derives from MainWindow, it then re-invokes MainWindow's constructor, when creates a thread, repeat ad infinitum.
Remember that when deriving, your constructor will always invoke the base class default constructor unless you explicitly instruct to use a different one.
As an aside, there are no use cases I can think of for ever deriving from MainWindow and this certainly isn't one. You will need to rethink your design.

C# threading - Lock Object

I am trying to lock a "boxed" object in a c# app, is this not possible?
class t
{
System.Object t_x = new object();
public t(int p)
{
t_x = p;
}
public void w()
{
lock (t_x)
{
for (int i = 0; i < 4; i++)
{
{
t_x = ((int)t_x) + 1;
Console.WriteLine(t_x);
Thread.Sleep(1000);
}
}
}
}
}
In another class I can start 2 threads:
Thread b1 = new Thread(new ThreadStart(t1.w));
b1.Start();
Thread b2 = new Thread(new ThreadStart(t1.w));
b2.Start();
However the portion is not locked.
When I lock an arbitrary object (i.e. one created and not modified as object a=new object()) it locks well.
Is boxing operation somehow "depromotes" my Object??
No, you can't do this - the lock block is shorthand for the following:
try(Monitor.Enter(lockObject))
{
//critical section
}
finally
{
Monitor.Exit(lockObject)
}
The documentation for Monitor.Enter states, "Use Monitor to lock objects (that is, reference types), not value types. When you pass a value type variable to Enter, it is boxed as an object. If you pass the same variable to Enter again, it is boxed as a separate object, and the thread does not block"
You need to create a separate lock object. The problem is that you re-assign t_x inside the loop. Assuming thread b1 gets inside the loop before b2 gets to the lock statement, b2 will be allowed inside the lock statement because, by that time, t_x will be a new object that does not have a lock on it.
The lock (t_x) call boxes an integer as a temporary object. Each call to lock(t_x) creates a New object and locking is useless.
(Lock expects an object and creates a NEW temporary object from the integer)
Just create a seperate lock object like said above by Femaref.
You have to use an extra object for the lock
object lockObj = new object();
public void foo()
{
lock(lockObj)
{
//do stuff here
}
}
If you really want (need?) to lock on the object, you can use a kind of wrapper :
public class IntWrapper
{
public int Value{get;set;}
}
Or if you need to stay more abstract :
public class ObjectWrapper
{
public Object Value { get;set; }
}
If you want to recognise when the data is loaded and also if the use tries to use it before then, you can do something like this:
Have a boolean flag like you mention, but use a separate object to lock before accessing it to prevent cross-thread race conditions.
When the user tries to use the data, if it is not loaded (check the variable) you can add another event handler to the worker RunWorkerCompleted event, that will immediately do what the user wants when the data is loaded.
Example:
public class MyClass
{
private bool dataIsReady = false;
private object locker = new object();
BackgroundWorker worker;
public void Begin()
{
worker = new BackgroundWorker();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lock (locker)
{
dataIsReady = true;
}
}
public void UseTriesToUseData()
{
lock (locker)
{
if (dataIsReady)
{
DoStuff();
}
else
{
this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoStuffCaller);
}
}
}
private void DoStuff()
{
// Do stuff with data.
}
private void DoStuffCaller(object sender, RunWorkerCompletedEventArgs e)
{
this.DoStuff();
}
}

Categories