Where Isolate can be defined in Flutter? How to start a thread in Flutter? - multithreading

I am a new in Flutter, so the question can be kind of obvious, but I can't find any answer on the Internet.
I have a Flutter application with some screens and I would say on the fifth screen I have a button, which should trigger some heavy computation work (converting thousands of images). On the same screen there is a progress bar and it is supposed to display the progress.
I am puzzled how to implement that technically. The triggering is happening obviously on onPressed of the button.
if I simply call a Future<void> function, then the UI is freezing completely for the time of processing, which is obviously is not desired behavior
if I put my function inside compute, on the first await I get exception
Unhandled Exception: Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first. which puzzles me, because I call WidgetsFlutterBinding.ensureInitialized() before runApp(). Anyway this method is not working.
compute(computationFunction, 'argument');
// ...
static void computationFunction(String argument) async {
await firstStepFunction();
// ...
if I put my function into Isolate.spawn I get exception Unhandled Exception: Invalid argument(s): Isolate.spawn expects to be passed a static or top-level function which is also puzzling me. I tried to make the function static and moved the function to the top level of this fifth screen module. Nothing changed. Am I supposed to start the Isolate at the main function? In all beautiful examples it is done like that. Can't I start the Isolate in the middle by the button click.
Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
void computationFunction(SendPort sendPort) async {
await firstStepFunction();
// ...
In Java I think a simple new Thread(...).start() will do the job.
But how to do it in Flutter?
Update:
In my experiments I've noticed, that neither Flutter Hot Restart nor Hot Reload are not working correctly with isolates. You need really to run again the whole app.
I managed to start Isolate.spawn all right if async/await keywords are removed. Off course the called function should have its synchronous version. So this does not work universally.
Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
static void computationFunction(SendPort sendPort) { // async removed
firstStepFunctionSync(); // the function is replaced with its synchronous version
// ...
I've found package flutter_isolate which allows to run the async functions:
FlutterIsolate.spawn(computationFunction, argument);
// ...
void computationFunction(SendPort sendPort) async {
await firstStepFunction();
// ...
I will try to use flutter_isolate package in my prototype.

You should read https://dev.to/alphamikle/why-should-you-use-isolates-in-flutter-1k5o, and look at package:isolates.
The article contrasts using main thread, compute, Isolate proper, and the isolates package, with advantages and disadvantages of each. Best article I've seen in a long time.
Also keep in mind, Java threads are data-shared, leading to possible deadlocks. Dart isolates are share-nothing, using "ports" to carefully move data between isolates, and no need for locking!

Check out this plugin, which provides an easy way to work with isolates with a worker abstraction or using Parallel methods, and has well-explained documentation.
https://pub.dev/packages/easy_isolate
The use is simple as
void main() async {
final worker = Worker();
await worker.init(mainHandler, isolateHandler);
worker.sendMessage(null);
}
void mainHandler(dynamic data, SendPort isolateSendPort) {
isolateSendPort.send(null);
}
// Top-level function (or static)
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
mainSendPort.send(null);
}
Or using the Parallel methods
Future main() async {
await Parallel.foreach(['test'], writeFile);
}
// Top-level function (or static)
void writeFile(String name) {
File(Directory.systemTemp.path + '/$name').createSync();
}

Related

How do you run a task in the background in flutter?

I have a Kotlin function that will take a while, and will then return a result (It downloads and parses a file).
I run it from Flutter like this:
void click() {
platform.invokeMethod('runMyLongFunc').then((a) {
print("Done");
setState(() {});
});
}
What should I do for this to run in a background thread (as of now it's blocking on the UI thread).
I tried making click async and it didn't help (void click() async).
There are a couple things you could try.
One is to have the Kotlin function do its work in a background thread using one of the methods Android offers for that (AsyncTask, for example). You could use a MethodChannel to handle the communication between the JVM and Dart, and have the Kotlin code send a message when it was done.
Another possibility is to use a Dart Isolate to multithread on the Dart side. You'd create an Isolate, make the call to Kotlin in its run method, and your other dart code could asynchronously wait for it to be finished on the UI thread while still running the event queue. The Flutter team has an example of how that might work.
void click() async{
var a = await platform.invokeMethod('runMyLongFunc');
print("Done");
setState(() {});
}
the await there is the key. Although it still runs on the single thread.

Parellel Task Library vs UI Thread Xamarin.Forms when constantly updating the UI

I am trying to make a stopwatch in Xamarin Forms and was wondering if I should use the native UI threading or Parallel Task Lib to constantly update the time label?
I tried to use the PT Lib, but I'm unable to get it to update my label, which makes me think that I should be using Native Threading, but I'm worry if I would be able to update the UI using a dependency service.
Is there a best practice for constantly updating the UI but still being able to execute other tasks such as button clicks?
UPDATE: I got this code below to work, but is this good practice? I am updating the time label constantly while still also bing able to press the lap buttons.
Stopwatch sw = new StopWatch();
bool inRace = false;
async void StartLapClick(object sender, System.EventArgs e)
{
if (!inRace)
{
inRace = true;
sw.Start();
updateTimer();
}
}
async void updateTimer()
{
await Task.Run(() =>
{
while(inRace)
{
string slc = sw.Elapsed.ToString();
Device.BeginInvokeOnMainThread(() =>
{
timerLbl.Text = slc;
});
Task.Delay(100).Wait();
}
});
}
No, your code has some issues (according to the Best Practices in Asynchronous Programming):
async void is bad - this will call your method in fire-and-forget fashion, you even can't get the errors from there. You should use it only for event handlers like StartLapClick, not for real methods like updateTimer
Task.Delay(100).Wait(); - Do not block on tasks, use await for this
replace the whole while loop with a simple timer, and remove the Task.Delay call
updateTimer(); - you're calling async method in synchronous fashion, which is also bad.
You have to update the UI from the UI thread. You could have a timer or something running in the background kicking out events periodically to be picked up and forwarded to the UI thread. Even if you did that, I don't know that the parallel task library is what you'd want to use. That's more focused on running many tasks... in parallel.
Try this:
Device.StartTimer(TimeSpan.FromSeconds(1.0), () => {
// Your code
};

Monotouch PerformSelector on specific thread with multiple arguments and callbacks

I've been having some issues with threading in monotouch. My app makes use of an external library which I've linked with and it works fine. Because of the nature of the app and the library I have to make all the calls to it on a single separate thread.These calls will generally be :
Random non deterministic caused by user
Every t miliseconds(around 20ms). Like an update function
After reading for a bit I decided to try out NSThread. I've managed to call the Update function by attaching an NSTimer to the thread's RunLoop and it's all working fine. The problem that I'm having now is calling other methods on the same thread. I read somewhere that using PerformSelector on the RunLoop adds the selector invocation to the RunLoop's queue and invokes it when available, which is basically exactly what I need. However the methods that I need to call :
Can have multiple paramteres
Have callbacks, which I need to invoke on the main thread, again with multiple parameters
For the multiple parameters problem I saw that NSInvocation can be a solution, but the life of me I can't figure out how to do it with monotouch and haven't found any relevant examples.
For the actuals calls that I need to make to the library, I tried doing a generic way in which I can call any function I choose via delegates on a particular thread, which sort of works until I'm hit with the multiple parameters and/or callbacks to the main thread again with multiple parameters. Should I maybe just register separate selectors for each (wrapped)function that I need to call from the library?
I'm not hellbent on using this approach, if there is a better way I'm open to it, it's just that after searching for other options I saw that they don't fit my case:
GCD(not even sure I have it in monotouch) spawns threads on it's own whenever necessary. I need a single specific thread to schedule my work on
NSInvocationQueue(which uses GCD internally from what I read) does the same thing.
pThreads, seem overkill and managing them will be a pain(not even sure I can use them in monotouch)
I'm not an iOS developer, the app works fine with monodroid where I had Runnables and Handlers which make life easier :) . Maybe I'm not looking at this the right way and there is a simple solution to this. Any input would be appreciated.
Thanks
UPDATE
I was thinking of doing something along these lines :
Have a simple wrapper :
class SelectorHandler : NSObject
{
public static Selector Selector = new Selector("apply");
private Action execute;
public SelectorHandler(Action ex)
{
this.execute = ex;
}
[Register("apply")]
private void Execute()
{
execute();
}
}
Extend NSThread
public class Daemon : NSThread
{
public void Schedule(Action action)
{
SelectorHandler handler = new SelectorHandler(action);
handler.PerformSelector(SelectorHandler.Selector, this, null, true);
}
}
Then, when I want to call something I can do it like this :
private Daemon daemon;
public void Call_Library_With_Callback(float param, Action<int> callback)
{
daemon.Schedule(() =>
{
int callbackResult = 0;
//Native library calls
//{
// Assign callback result
//}
daemon.InvokeOnMainThread(() =>
{
callback(callbackResult);
});
});
}

How can I implement callback functions in a QObject-derived class which are called from non-Qt multi-threaded libraries?

(Pseudo-)Code
Here is a non-compilable code-sketch of the concepts I am having trouble with:
struct Data {};
struct A {};
struct B {};
struct C {};
/* and many many more...*/
template<typename T>
class Listener {
public:
Listener(MyObject* worker):worker(worker)
{ /* do some magic to register with RTI DDS */ };
public:
// This function is used ass a callback from RTI DDS, i.e. it will be
// called from other threads when new Data is available
void callBackFunction(Data d)
{
T t = extractFromData(d);
// Option 1: direct function call
// works somewhat, but shows "QObject::startTimer: timers cannot be started
// from another thread" at the console...
worker->doSomeWorkWithData(t); //
// Option 2: Use invokeMethod:
// seems to fail, as the macro expands including '"T"' and that type isn't
// registered with the QMetaType system...
// QMetaObject::invokeMethod(worker,"doSomeGraphicsWork",Qt::AutoConnection,
// Q_ARG(T, t)
// );
// Option 3: use signals slots
// fails as I can't make Listener, a template class, a QObject...
// emit workNeedsToBeDone(t);
}
private:
MyObject* worker;
T extractFromData(Data d){ return T(d);};
};
class MyObject : public QObject {
Q_OBJECT
public Q_SLOTS:
void doSomeWorkWithData(A a); // This one affects some QGraphicsItems.
void doSomeWorkWithData(B b){};
void doSomeWorkWithData(C c){};
public:
MyObject():QObject(nullptr){};
void init()
{
// listeners are not created in the constructor, but they should have the
// same thread affinity as the MyObject instance that creates them...
// (which in this example--and in my actual code--would be the main GUI
// thread...)
new Listener<A>(this);
new Listener<B>(this);
new Listener<C>(this);
};
};
main()
{
QApplication app;
/* plenty of stuff to set up RTI DDS and other things... */
auto myObject = new MyObject();
/* stuff resulting in the need to separate "construction" and "initialization" */
myObject.init();
return app.exec();
};
Some more details from the actual code:
The Listener in the example is a RTI DataReaderListener, the callback
function is onDataAvailable()
What I would like to accomplish
I am trying to write a little distributed program that uses RTI's Connext DDS for communication and Qt5 for the GUI stuff--however, I don't believe those details do matter much as the problem, as far as I understood it, boils down to the following:
I have a QObject-derived object myObject whose thread affinity might or might not be with the main GUI thread (but for simplicity, let's assume that is the case.)
I want that object to react to event's which happen in another, non-Qt 3rd-party library (in my example code above represented by the functions doSomeWorkWithData().
What I understand so far as to why this is problematic
Disclaimer: As usual, there is always more than one new thing one learns when starting a new project. For me, the new things here are/were RTI's Connext and (apparently) my first time where I myself have to deal with threads.
From reading about threading in Qt (1,2,3,4, and 5 ) it seems to me that
QObjects in general are not thread safe, i.e. I have to be a little careful about things
Using the right way of "communicating" with QObjects should allow me to avoid having to deal with mutexes etc myself, i.e. somebody else (Qt?) can take care of serializing access for me.
As a result from that, I can't simply have (random) calls to MyClass::doSomeWorkWithData() but I need to serialize that. One, presumably easy, way to do so is to post an event to the event queue myObject lives in which--when time is available--will trigger the execution of the desired method, MyClass::doSomeWorkWithData() in my case.
What I have tried to make things work
I have confirmed that myObject, when instantiated similarly as in the sample code above, is affiliated with the main GUI thread, i.e. myObject.thread() == QApplication::instance()->thread().
With that given, I have tried three options so far:
Option 1: Directly calling the function
This approach is based upon the fact that
- myObject lives in the GUI thread
- All the created listeners are also affiliated with the GUI thread as they are
created by `myObject' and inherit its thread that way
This actually results in the fact that doSomeWorkWithData() is executed. However,
some of those functions manipulate QGraphicsItems and whenever that is the case I get
error messages reading: "QObject::startTimer: timers cannot be started from another
thread".
Option 2: Posting an event via QMetaObject::invokeMethod()
Trying to circumvent this problem by properly posting an event for myObject, I
tried to mark MyObject::doSomeWorkWithData() with Q_INVOKABLE, but I failed at invoking the
method as I need to pass arguments with Q_ARG. I properly registered and declared my custom types
represented by struct A, etc. in the example), but I failed at the fact the
Q_ARG expanded to include a literal of the type of the argument, which in the
templated case didn't work ("T" isn't a registered or declared type).
Trying to use conventional signals and slots
This approach essentially directly failed at the fact that the QMeta system doesn't
work with templates, i.e. it seems to me that there simply can't be any templated QObjects.
What I would like help with
After spending about a week on attempting to fix this, reading up on threads (and uncovering some other issues in my code), I would really like to get this done right.
As such, I would really appreciate if :
somebody could show me a generic way of how a QObject's member function can be called via a callback function from another 3rd-party library (or anything else for that matter) from a different, non QThread-controlled, thread.
somebody could explain to me why Option 1 works if I simply don't create a GUI, i.e. do all the same work, just without a QGraphcisScene visualizing it (and the project's app being a QCoreApplication instead of a QApplication and all the graphics related work #defineed out).
Any, and I mean absolutely any, straw I could grasp on is truly appreciated.
Update
Based on the accepted answer I altered my code to deal with callbacks from other threads: I introduced a thread check at the beginning of my void doSomeWorkWithData() functions:
void doSomeWorkWithData(A a)
{
if( QThread::currentThread() != this->thread() )
{
QMetaObject::invokeMethod( this,"doSomeWorkWithData"
,Qt::QueuedConnection
,Q_ARG(A, a) );
return;
}
/* The actual work this function does would be below here... */
};
Some related thoughts:
I was contemplating to introduce a QMutexLocker before the if statement, but decided against it: the only part of the function that is potentially used in parallel (anything above the return; in the if statement) is--as far as I understand--thread safe.
Setting the connection type manually to Qt::QueuedConnection: technically, if I understand the documentation correctly, Qt should do the right thing and the default, Qt::AutoConnection, should end up becoming a Qt::QueuedConnection. But since would always be the case when that statement is reached, I decided to put explicitly in there to remind myself about why this is there.
putting the queuing code directly in the function and not hiding it in an interim function: I could have opted to put the call to invokeMethod in another interim function, say queueDoSomeWorkWithData()', which would be called by the callback in the listener and then usesinvokeMethodwith anQt::AutoConnection' on doSomeWorkWithData(). I decided against this as there seems no way for me to auto-code this interim function via templates (templates and the Meta system was part of the original problem), so "the user" of my code (i.e. the person who implements doSomeWorkWithData(XYZ xyz)) would have to hand type the interim function as well (as that is how the templated type names are correctly resolved). Including the check in the actual function seems to me to safe typing an extra function header, keeps the MyClass interface a little cleaner, and better reminds readers of doSomeWorkWithData() that there might be a threading issue lurking in the dark.
It is ok to call a public function on a subclass of QObject from another thread if you know for certain that the individual function will perform only thread-safe actions.
One nice thing about Qt is that it will handle foreign threads just as well as it handles QThreads. So, one option is to create a threadSafeDoSomeWorkWithData function for each doSomeWorkWithData that does nothing but QMetaMethod::invoke the non-threadsafe one.
public:
void threadSafeDoSomeWorkWithData(A a) {
QMetaMethod::invoke("doSomeWorkWithData", Q_ARG(A,a));
}
Q_INVOKABLE void doSomeWorkWithData(A a);
Alternatively, Sergey Tachenov suggests an interesting way of doing more or less the same thing in his answer here. He combines the two functions I suggested into one.
void Obj2::ping() {
if (QThread::currentThread() != this->thread()) {
// not sure how efficient it is
QMetaObject::invoke(this, "ping", Qt::QueuedConnection);
return;
}
// thread unsafe code goes here
}
As to why you see normal behaviour when not creating a GUI? Perhaps you're not doing anything else that is unsafe, aside from manipulating GUI objects. Or, perhaps they're the only place in which your thread-safety problems are obvious.

Silverlight - Waiting for asynchronous call to finish before returning from a method

I have a Silverlight application that uses WCF services and also uses the Wintellect Power Threading library to ensure logic executes fully before the application continues. This is achieved by calling back to the application using delegates so it can continue after the service call has completely finished.
I wish to achieve the same thing in another part of my application but without the use of callbacks e.g. call method that uses WCF service to say load an object from the database, wait for this to return and then return the Id of the object from the original method called.
The only way I could see to do this was to carry out the call to the WCF service in a helper library which loads the object on a different thread and the original method would keep checking the helper library (using static variables) to wait for it to complete and then return it.
Is this the best way to achieve this functionality? If so here are details of my implementation which is not working correctly.
public class MyHelper
{
private static Thread _thread;
private static User _loadedObject;
public static GetUser()
{
return _loadedObject;
}
public static void LoadObject(int userId)
{
_loadedObject = null;
ParameterizedThreadStart ts = new ParameterizedThreadStart(DoWork);
_thread = new Thread(ts);
_thread.Start(userId);
}
private static void DoWork(object parameter)
{
var ae = new AsyncEnumerator();
ae.BeginExecute(DoWorkWorker(ae, Convert.ToInt32(parameter)), ae.EndExecute);
}
private static IEnumerator<Int32> DoWorkWorker(AsyncEnumerator ae, int userId)
{
// Create a service using a helper method
var service = ServiceHelper.GetService<IUserServiceAsync>();
service.BeginGetUserById(userId, ae.End(), null);
yield return 1;
_loadedObject = service.EndGetUserById(ae.DequeueAsyncResult());
_thread.Abort();
}
}
My method then is:
public int GetUser(int userId)
{
MyHelper.LoadObject(userId);
User user = MyHelper.GetUser();
while (user == null)
{
Thread.Sleep(1000);
user = MyHelper.GetUser();
}
return user.Id;
}
The call to the get the user is executed on a different thread in the helper method but never returns. Perhaps this is due to the yield and the calling method sleeping. I have checked the call to get the user is on a different thread so I think everything should be kept separate,
The whole construct you are using does not match current best practices of Silverlight. In Silverlight your data access methods (via WebServices of course) are executed asynchronously. You should not design around that, but adapt your design accordingly.
However calling services sequentially (which is different than synchonously) can be valid in some scenarios. In this blog post I have shown how to achieve this by subscribing the Completed event of the remote call and block the UI in the meantime, with which the workflow looks and feels like normal async calls.
I believe calls to the server from Silverlight apps use events that fire on the UI thread; I think that's part of the Silverlight host environment in the browser and can't be worked around. So trying to call back to the server from another thread is never going to end well. If you are waiting in program code in the UI thread, your never going to get the call result events from your WCF calls.
You can simulate a synchronous call from a non-UI thread with a callback on the UI thread, but that is probably not what you want. It's better to bite the bullet and make your program logic work with the async calls Silverlight gives you.
If you code against the Interface created for your service reference you can call the Begin and End methods 'synchronously' for each one of your service calls, we then pass in an Action<T> to execute after the End methods has completed. Take note that you have to do this from a dispatcher. This is very close to making a synchronous call as the code to run after the call is still written where the call is made, and it executes after the service call is completed. It does however involve creating wrapper methods but we also worked around that by hiding our wrappers and generating them automatically. Seems like a lot of work but isn't, and ends up being more elegant than all the event handlers etc. Let me know if you need more info on this pattern

Resources