I have a System.Threading.Timer
that receives some data each second, and I want its callback to put the received data into an object, which should be returned to client code. I tried to implement that using C#’s reference types mechanism. Here’s the current callback:
private void Receive(object? obj) {
var data = GetData();
obj = (object)data; // Does nothing
}
And here’s the method that starts the timer (timer
is a field):
public void Start(Image image) {
timer = new Timer(
Receive,
image,
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(1)
);
}
Expected behavior: each second the data (which is image data) is received and put to the image, the reference to which is specified in parameter image
.
Actual behavior: when I defined a variable of type Image
, passed it to the Start
method and checked its value (using the Visual Studio debugger) after 5 seconds, it was the empty Bitmap
I assigned to it at the very start.
I have also checked that the data (containing non-empty image) is sent properly.
Can anybody tell me what’s the issue, or, alternatively, suggest a different way to change the "state" object in a timer callback?
2
Answers
In the Recieve method you get an obj as input reference. Then you try to set a value to obj. This last step cannot work, since you work on the reference.
The only way it could work is, if the input reference is given as a ref, which is
Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller.
obj = (object)data;
only sets the data locally in theReceive
method. You could fix it by creating a wrapper objectThen create a static instance of it that can be accessed from where you need it
Depending on where you must access the images it can also an instance member and be private; however, it must always be the same instance that you used to start the timer.
Then start like this
And receive
Now, you can access the new images through the static
Data.Image
property.Alternatively, you could "consume" the image (i.e., display or whatever you are doing with it) directly in the
Receive
callback and ignore theobj
parameter. However, I do not have enough context information (where do the different code pieces reside and in which thread are they executed, etc.).Update
The Image will be
null
inImageData
first, until the Receiver callback is called for the first time.If you want an image at the very start, you can do several things.
Start the timer with the first time argument as 0 (this is the time it waits until firing the first time).
Call the callback directly in
Start
:Receive(Data)
.Assign an image with
Data.Image = GetData();
.Initialize the image in
ImageData
with a dummy image (maybe some kind of wait image).Update 2
Apparently you are creating a library. I assume the library will pull images regularly and make them available to a consumer.
I suggest exposing an event in the library any consumer can subscribe to to receive images.
In the library we declare event arguments as
A possible implementation of the library:
The consumer code:
If you want to stop the timer, you can do so by calling
library.Dispose();
. This also ensures that the timer resources are disposed properly.