Run Multiple UI Threads

Multiple UI Threads with one Working Thread

There is usually only 1 UI thread. If you want to have longer running tasks (especially if you want them to keep going regardless of the UI) then you should use a service. The service can to it's work in the background even when the app is closed. This way you can have one class responsible for all of your network traffic and data collection and all of your activities can just bind to that.

Run multiple UI Threads

I don't think that what you ask is really what you want but creating a message pump per thread is easy, you just have to call Application.Run once per thread.

static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Thread t1 = new Thread(Main_);
Thread t2 = new Thread(Main_);

t1.Start();
t2.Start();

t1.Join();
t2.Join();
}

static void Main_()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

Multiple UI threads on the same window

You could make a thread-safe Producer/Consumer queue of delegates.
Any thread that wants to update a UI component would create a delegate encapsulating the operations to be performed, and add it to the queue.
The UI thread (assuming all components were created on the same thread) would then periodically pull an item from the queue, and execute the delegate.

Running java Application with Multiple UI Threads

There are several problems with the structure of the application as you have posted it in the question.

The Application class represents the lifecycle of the entire application, which is managed via calls to its init(), start() and stop() methods. There should be only one Application instance in the entire application, and typically this instance is created by the JavaFX startup mechanism so you should not instantiate the Application subclass yourself.

JavaFX applications require the JavaFX runtime to be started, which includes launching the JavaFX Application Thread. This is done via a call to the static Application.launch() method, which must be called only once. The launch() method starts the JavaFX runtime, creates the instance of the Application class, calls init(), and then calls start() on the FX Application Thread. (In JavaFX 9 and later, you can also start the runtime by calling Platform.startup(), but use cases for this are rare).

Note that in your application, there is no call to Application.launch() (or Platform.startup()), so the JavaFX runtime is never started.

Certain operations can only be performed on the FX Application Thread. These include creating Stages and Scenes, and any modifications of properties of UI elements that are already displayed. Thus you cannot "run each client" in a separate thread. This is the cause of your exception: you are trying to create a new Stage on a thread that is not the FX Application Thread.

Each client does not need a new thread to display the UI (and, as described above, cannot do that). You likely do need to perform each client's communication with the server on a separate thread (because those are operations that take a long time, and you should not block the FX Application thread). You can do that by creating a new thread for each client's server communication, or using a shared executor service so that each client can get a thread from a pool (this is probably the preferred approach).

So your structure should look something like this:

public class Client {

private Parent ui ;
private ExecutorService exec ; // for handling server communication

private final String user ;

public Client(String user, ExecutorService exec) {
this.user = user ;
this.exec = exec ;
try {
ui = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
}

public Client(String user) {
this(user, Executors.newSingleThreadExecutor());
}

public Parent getUI() {
return ui ;
}

public void showInNewWindow() {
Scene scene = new Scene(ui);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}

public void checkForNewEmail() {
Task<List<Email>> newEmailTask = new Task<>() {
@Override
protected List<Email> call() throws Exception {
List<Email> newEmails = new ArrayList<>();
// contact server and retrieve any new emails
return newEmails ;
}
};
newEmailTask.setOnSucceeded(e -> {
List<Email> newEmails = newEmailTask.getValue();
// update UI with new emails...
});
exec.submit(newEmailTask);
}

// etc ...
}

Then your ClientController class could do something like this:

public class ClientController {

private ExecutorService exec = Executors.newCachedThreadPool();

public void startClients() {
Client clientA = new Client("aaaa@gmail.com", exec);
Client clientB = new Client("bbbb@gmail.com", exec);
clientA.showInNewWindow();
clientB.showInNewWindow();
}
}

and your app class can do

public class ClientApp extends Application {

@Override
public void start(Stage primaryStage) {
ClientController controller = new ClientController();
controller.startClients();
}

public static void main(String[] args) {
Application.launch(args);
}
}

Multiple UI Threads - Winforms

Messing with threads will only bite you sooner or later.

From MSDN:

Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread

You can of course use as many threads as you like, but don't try to create a workaround to be able to use different threads for updating the UI. Use Invoke/InvokeRequired from your worker/background threads instead.

Using an extension method makes it cleaner: Automating the InvokeRequired code pattern

WPF UI on multiple threads?

In general, it "works". The main thing you have to do is set the thread to STA (as in your example). But you gain little or nothing by running some of the UI in a different thread. Each thread can still be blocked by long-running tasks, so you still need to execute those in yet another thread, and you still have the cross-thread issue requiring some kind of marshaling back to the UI thread (e.g. Dispatcher.Invoke()).

Furthermore, with more than one UI thread, now not only do you have to keep track of which UI thread goes with which UI object (since they still can be used only with the thread that owns them), you will have more problems with UI objects interacting with each other, because those owned by different threads are mutually exclusive. Each is required to be accessed only in the thread in which it's owned, so the only way to have them work together is to create some kind of proxy system to pass data and events back and forth between threads.

Basically, it never was and still is not a good idea to create more than one thread for the UI.

Fortunately, as of .NET 4.5 and C# 5.0, there are framework and language features that greatly simplify the handling of background operations and the marshaling of information back to the UI thread. With the async/await feature, you can initiate asynchronous operations with framework features like the Task<T> class or certain class methods (usually with names ending in the word Async), have the UI thread unblocked for the duration of the operation, and yet easily write code to handle whatever work has to be done at the end of the operation.

There is also the Progress<T> class, which implements the IProgress<T> interface in a way that is convenient for dealing with UI progress updates, i.e. invokes the callback on the UI thread (as long as you create the Progress<T> instance in the UI thread, of course).

So, take the path that .NET and C# are encouraging you to take, and avoid the one that is hard. Keep all your UI in a single thread, and solve whatever issues come up using the tools provided instead of trying to fight the API. :)

Multiple threads tasks updating 1 progressbar - UI C# WPF

You could use either the Parallel class or the PLINQ library to process the items in parallel, using multiple ThreadPool threads. For reporting the progress to the UI you could use the IProgress<T> abstraction, where T can be any type of your choice. For example it could be a ValueTuple<string, bool>, in order to communicate both the processed item, and the success/failure of the operation. This way you could create an application-agnostic, library-like method. You could copy-paste this method verbatim in a completely different application (a Console app for example), and it would work exactly the same without any modification. Below is an example of such a method, that uses the PLINQ library for handling the parallelism:

public static string[] ProcessAllItems(string[] items, string baseUrl,
IProgress<(string, bool)> progress)
{
return items
.AsParallel()
.AsOrdered()
.WithDegreeOfParallelism(4)
.Select(item =>
{
HttpRequest req = new HttpRequest();
req.Proxy = null;
req.ConnectTimeout = 5000;
req.IgnoreProtocolErrors = true;
var response = req.Get(baseUrl + url);
if (response.StatusCode == Leaf.xNet.HttpStatusCode.OK)
{
progress.Report((item, true)); // Success
return item;
}
progress.Report((item, false)); // Failure
return null;
})
.Where(result => result != null)
.ToArray();
}

Then all that you'll have to do is to create a Progress<(string, bool)> object on the UI thread, and pass a delegate that handles the reported messages from the background thread. This delegate should update both the myResults and progressbar UI elements. Calling the ProcessAllItems should be wrapped in an await Task.Run, in order to keep the UI responsive.

private async void startButton_Click(object sender, RoutedEventArgs e)
{
string baseUrl = myURL.Text;
string[] items = myLoadedList.Items.Select(x => x.ToString()).ToArray();
var completedCount = 0;

var progress = new Progress<(string, bool)>(message =>
{
if (message.Item2)
{
myResults.Items.Add(message.Item1);
}
completedCount++;
progressbar.Value = completedCount * 100 / items.Length;
});

progressbar.Value = 0;
myResults.Items.Clear();
myResults.Items.Add("----Starting----");

string[] results = await Task.Run(() =>
{
return ProcessAllItems(items, baseUrl, progress);
});

progressbar.Value = 100;
myResults.Items.Add("----Finish----");
}

Notice the async keyword in the startButton_Click handler, that enables the use of the await operator.

The main point of this suggestion is to avoid using the awkward Dispatcher.Invoke method, that encourages intermingling the processing logic with the presentation logic, as well as the technologically obsolete BackgroundWorker class.



Related Topics



Leave a reply



Submit