Skip To Content

Threading considerations

In this topic

When developing your ArcGIS Runtime SDK app, strive to provide responsive interaction with the user, even when your app is doing other work. Using multiple threads of execution is one of the most powerful ways to keep your app responsive while using the processor in between or even during user events to perform other tasks. Although the process of writing multithreaded applications has been made significantly easier in recent versions of .NET, great care must still be taken when architecting these applications to ensure the integrity of the application's UI and of objects created on the UI thread.

It's important to realize that even without explicitly creating additional threads, some code in your ArcGIS Runtime SDK for .NET app may run on a background thread. Working with asynchronous tasks, for example, executes on a thread in the app's thread pool. An understanding of threading and the relationship between threads and objects in the UI can help avoid some common pitfalls.

Threading overview

An app runs inside memory space that is allocated by the operating system, called a process. Inside of that process, one or several autonomous units called threads are responsible for executing code as the app runs. The processor divides its time among all threads, which are alternately paused and resumed as processor time expires or is made available. Each thread maintains the information it needs to resume seamlessly when it is called to execute. As the amount of code running on a single thread increases, more processor cycles are required to complete its work. At some point, performance for the app is impacted.

Every app with a user interface has a thread that is responsible for displaying UI elements and for handling user interaction with the app. This thread is generically referred to as the UI thread. The UI thread is your pipeline to the user interface and is the only thread you can use to surface information to the user. It also has the potential of being overworked, which immediately impacts the responsiveness of your app from the user's perspective.

For more details about working with threads in .NET, see MSDN for Managed threading basics and Managed threading best practices.

Run code on background threads

To reduce the load on the UI thread, choose to run certain tasks on other (non UI) threads, referred to as background threads. Operations that are particularly time consuming or processor intensive are good candidates to run on a background thread. When code runs on more than a single thread in your app, you are working with a multithreaded app.

Each process has a set of background threads managed by the operating system, referred to as the thread pool. You can work with the thread pool for your app using static methods on the System.Threading.ThreadPool class. To execute an operation on a (random) thread in the pool, use the static ThreadPool.QueueUserWorkItem method and pass in the code to run, as shown in the following example.

private void RunOnBackgroundThread()
    // create a WaitCallback that points to a function in my project
    // the function must have the signature: void function(object param)
    var op = new System.Threading.WaitCallback(SquareIt);

    // run SquareIt on a thread in the pool, pass in an input value of 12
    System.Threading.ThreadPool.QueueUserWorkItem(op, 12);

private long answer; // field to store the result from SquareIt

// function to square the input (takes 'object' param to conform with signature required by System.Threading.WaitCallback)
private void SquareIt(object input)
    var num = 0;
    int.TryParse(input.ToString(), out num);
    this.answer = num * num;

You can also create threads explicitly by creating a new System.Threading.Thread. The following example creates a new thread to run the same SquareIt function. The input value (12) is specified when the thread starts.

private void RunOnBackgroundThread()
    // create a ParameterizedThreadStart object that refers to the SquareIt function
    var threadStart = new System.Threading.ParameterizedThreadStart(SquareIt);

    // create a new thread to run the code
    var thread = new System.Threading.Thread(threadStart);

    // start the thread and pass in 12 as the value of the required parameter

Running an asynchronous Task also executes code on a background thread. The following example runs the function from a Task.

private async void RunOnBackgroundThread()
    // call the static Task.Run method to create a Task to run a function in the background
    var backgroundTask = Task.Run(() => { SquareIt(12); });

    // await completion of the Task
    await backgroundTask;

Run code on the UI thread

As tempting as it may be to always move execution to a background thread to maintain UI responsiveness, some operations must take place on the UI thread. A common example is surfacing UI elements to the user (such as a message box or dialog box) or working with elements created on the UI thread (such as an Esri.ArcGISRuntime.Layers.GraphicsLayer). If you attempt to run such code from a background thread, you may receive an exception like the following: The application called an interface that was marshalled for a different thread or Access is denied. Fortunately, the System.Windows.Threading.Dispatcher object provides a way to access the UI thread for your app.

Any object that inherits from System.Windows.Threading.DispatcherObject has a Dispatcher property that gives you access to the thread on which it runs. This includes objects of type System.Windows.DependencyObject and System.Windows.Application, which means you can access the Dispatcher through the app or from any of its UI elements. To run code on the thread associated with the Dispatcher, call the InvokeAsync method and pass in the code to execute.

The following example is taken from an app that shows a browser window to allow the user to enter a user name and password for OAuth authentication. See the ArcGIS Online Services > Traffic sample in the ArcGIS Runtime SDK samples for .NET for the complete code. The Dispatcher for the current app is used to asynchronously invoke code to run on the UI thread.

public Task<IDictionary<string, string>> AuthorizeAsync(string serviceUri, string authorizeUri, string callbackUri)
	   _tcs = new TaskCompletionSource<IDictionary<string, string>>();
    // get the Dispatcher for the current application
    var dispatcher = Application.Current.Dispatcher;

    // if no dispatcher or are on the same thread, run code directly
    if (dispatcher == null || dispatcher.CheckAccess())
        // invoke the action on the dispatcher's thread
        dispatcher.InvokeAsync(() => AuthorizeOnUIThread(authorizeUri));

    return _tcs.Task;

private void AuthorizeOnUIThread(string authorizeUri)
   	// ... display the login page at authorizeUri in the app's UI ...

You may also need to use the Dispatcher to update UI controls based on information coming from a background thread. A common example is an event handler receiving data from a sensor (updated locations coming from a GPS device, for example). If you want to update UI controls, such as TextBlocks, or show the information in a message box, you may need to run that code on the UI thread.

The following example updates text content in the UI when an updated location is received from a LocationProvider.LocationChanged event.

// a handler for the MapView.LocationDisplay.LocationProvider.OnLocationChanged event
void OnLocationChanged(object sender, LocationInfo e)
    var thisLocation = e.Location;
    var locationText = "X: " + thisLocation.X.ToString() +
                                        " Y: " + thisLocation.Y.ToString();

    // use the map view's dispatcher to update a TextBlock in the UI
    this.MyMapView.Dispatcher.InvokeAsync(() => this.CurrentLocationTextBlock.Text = locationText);

ArcGIS Runtime automatically marshals results from a background thread to the UI in many cases, such as updating the display of the current location as it comes from the location provider.

Background threads with graphics

Most applications built with the ArcGIS Runtime involve the processing of information and the generation of results to display to the user on a map. Typically, when the results are temporary they display as graphics in a graphics layer. The GraphicsLayer exists within the Map's layer collection hosted on the UI by the MapView component. As the volume of information increases, the processing time increases until at some point the user interface stops responding to interaction because the thread that should be managing the UI and responding to events is busy running your application code. The solution to this problem is usually to move the time consuming operation to a separate thread, leaving the UI thread free to perform other work.

GraphicsLayer inherits from DependencyObject and therefore must always be created on the UI thread. This means you can only modify the GraphicsLayer on the UI thread. For example, if you want to efficiently add a number of graphics to the graphics layer by calling the GraphicsLayer.Graphics.AddRange method, this must be done on the UI thread. Other objects that are part of the GraphicsLayer can also only be modified on the UI thread, including the GraphicsLayer.Renderer (if one exists), the GraphicsLayer.Graphics, Graphic.Geometry, and Graphic.Symbol. There are specific checks within the API and an exception is thrown if any attempt is made to modify these objects from a different thread to the one on which they were created.


Attempting to work with graphics layer content from a background thread throws a System.InvalidOperationException, with a message like the following: The calling thread cannot safely modify content of a GraphicsLayer. GraphicsLayer content should be modified on the thread associated with the GraphicsLayer.Dispatcher instance.

However, other objects in the API are not associated with a Dispatcher instance and therefore, new instances can be created on one thread and passed to another. This includes graphics not added to a layer. Therefore, it is acceptable to create a collection of Graphic objects on a thread, pass them to the UI thread, then add them to the GraphicsLayer. Once part of the layer, they can only be modified on the UI thread.

The following example creates thousands of graphics with complex geometries on a background thread and displays them on the UI thread.

private async void DrawBuffersButton_Click(object sender, RoutedEventArgs e) 
    // kick off creation of a large number of complex graphics on a background thread
    var graphics = await Task.Run<List<Graphic>>((Func<List<Graphic>>)CreateGraphics);

    // ... resumes back here on the UI thread once the task is complete
    // Now add the graphicss
    var graphicsOverlay = new GraphicsOverlay();
    graphicsOverlay.RenderingMode = GraphicsRenderingMode.Static; // better for large numbers of graphics

Random random = new Random();
// function to create 10,000 graphics from a random geodesic buffer
private List<Graphic> CreateGraphics()
    var graphics = new List<Graphic>();
    // generate 10,000 unique graphics and return them at the end in a list
    for (int i = 0; i < 10000; i++)
        // use a random color (and semi-transparency) for each graphic
        var randomColor = Color.FromArgb(75, (byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
        var symbol = new SimpleFillSymbol() { Style = SimpleFillStyle.Solid, Color = randomColor };

        // create a location (MapPoint) using random coords for Web Mercator
        var centerPoint = new MapPoint(
            (random.NextDouble() * 40000000) - 20000000,
            (random.NextDouble() * 40000000) - 20000000, SpatialReferences.WebMercator);

        // use a random buffer distance
        double distance = random.NextDouble() * 500000 + 10000;

        // Use a geodesic buffer, this returns a polygon of equal ground distance around the center point
        // (circles nearer the poles appear larger on a WebMercator map)
        var geodesicCircle = GeometryEngine.GeodesicBuffer(centerPoint, distance, LinearUnits.Meters);

        // create a graphic to display the buffer geometry; add it to the list
        var graphic = new Graphic(geodesicCircle, symbol);

    return graphics;
Map with ten thousand geodesic buffer graphics

This concept becomes increasingly beneficial for the more complex geometries like polylines and polygons. These geometries can be constructed in the background on a separate thread, possibly using some of the GeometryEngine methods, passed to the UI thread, and set into graphics.

Another consideration is changes to graphics are not immediately rendered in the map. There is a batching mechanism that picks up all changed content and pushes it into the rendering pipeline in one operation. This means that when building graphics, all properties should be set first before adding the Graphic to the GraphicsLayer. Knowing this should determine where and when you create objects that become part of a GraphicsLayer.

Related topics