> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/core/get-started/samples/multithreadedtest.md).

# Multithreaded PDF Reading - Multithreaded

Illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model. Multiple threads can read the document at the same time, but only one t

Illustrates how to use PDFDoc locking mechanisms to access the document concurrently. PDFDoc uses a recursive shared lock model. Multiple threads can read the document at the same time, but only one thread can write to the document. A given thread can acquire as many locks of the same type as it wants, in a recursive fashion. Learn more about our [Server SDK](/core/get-started/frameworks/dotnet.md).

{% tabs %}
{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2024 by Apryse Software Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.	 
//---------------------------------------------------------------------------------------

using System;
using System.Drawing;

using pdftron;
using pdftron.Common;
using pdftron.PDF;
using pdftron.SDF;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace PDFDrawTestCS
{
	/// <summary>
	//---------------------------------------------------------------------------------------
	// The following sample illustrates how to use PDFDoc locking mechanisms to access the document
	// concurrently.  PDFDoc uses a recursive shared lock model.  Multiple threads can read the document 
	// at the same time, but only one thread can write to the document.  A given thread can acquire 
	// as many locks of the same type as it wants, in a recursive fashion.
	//
	// It's important to note that you cannot upgrade a PDFDoc read lock to a write lock.  Because of
	// the nature of the lock, this would cause a deadlock, and is not allowed.
	//
	// If a thread attempts to acquire a write lock while holding a read lock, an exception will be
	// thrown.  As a side effect, this means certain PDFNet API calls which implicitly acquire a write
	// lock on the document will throw an exception if called while holding a read lock.  The API
	// documents which methods acquire a write lock on the document.
	//
	// For more information on document locking in PDFNet, please consult the following knowledge base
	// article: https://groups.google.com/forum/#!topic/pdfnet-sdk/pDZsqrjCEdg
	//---------------------------------------------------------------------------------------

	class Class1
	{
		private static pdftron.PDFNetLoader pdfNetLoader = pdftron.PDFNetLoader.Instance();
		static Class1() {}
		
		/// <summary>
		/// A static method for rasterizing a given page to a file.
		/// </summary>
		static void DoDraw(PDFDoc doc, Page pg, string output_path)
		{
			//acquire a read lock, since we will be accessing the document.
			doc.LockRead();

			//Acquiring a write lock when holding a read lock is illegal,
			//and would cause an exception to be thrown:
			//doc.Lock();

			//draw the page
			using (PDFDraw draw = new PDFDraw())
			{
				draw.SetDPI(92);
				draw.Export(pg, output_path);
				Console.WriteLine("Rendered page: Result saved in {0}", output_path);
			}

			//release the read lock
			doc.UnlockRead();
		}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		static void Main(string[] args)
		{
			// The first step in every application using PDFNet is to initialize the 
			// library and set the path to common PDF resources. The library is usually 
			// initialized only once, but calling Initialize() multiple times is also fine.
			PDFNet.Initialize(PDFTronLicense.Key);				   
			
			// Relative path to the folder containing test files
			string input_path =  "../../../../TestFiles/";
			string output_path = "../../../../TestFiles/Output/";

			//Example - Exporting images of each page in the document in parallel,
			//          and annotating the document in the main thread.
			try  
			{
				// Open the PDF document.
				using (PDFDoc doc = new PDFDoc(input_path + "newsletter.pdf")) 
				{
					// Lock the document, since we are going to annotate it in this thread.
					doc.Lock();

					// Initialize the security handler, in case the PDF is encrypted.
					doc.InitSecurityHandler();

					List<Task> tasks = new List<Task>();

					// Iterate through each page in the document
					for (PageIterator itr=doc.GetPageIterator(); itr.HasNext(); itr.Next()) 
					{
						//note that locking a second time does not cause a deadlock:
						doc.Lock();

						// You can acquire a read lock while holding a write lock:
						doc.LockRead();
						doc.UnlockRead();

						// Choose an output path for this page
						string page_output_path = output_path + "newsletter_" + itr.GetPageNumber() + ".png";

						// Create an asynchronous task to draw the page
						Page pg = itr.Current();
						Task t = new Task(() => DoDraw(doc, pg, page_output_path));
						// Start the Task (although it won't be able to access the document, since we have a write lock)
						t.Start();
						// Add it to our list of Tasks so we can Wait() for it later.
						tasks.Add(t);

						Console.WriteLine("Adding stamp to PDFDoc, page " + itr.GetPageNumber());

						// Create a stamp annotation (See AnnotationTest for more details)
						pdftron.PDF.Annots.RubberStamp stamp = pdftron.PDF.Annots.RubberStamp.Create(doc.GetSDFDoc(), new Rect(30, 30, 300, 200));
						stamp.SetIcon("Approved");
						itr.Current().AnnotPushBack(stamp);

						// Releasing the lock decrements our lock count, 
						// but the thread still holds a write lock on the document:
						doc.Unlock();
					}
	 
					// Now we release the write lock, and the 
					// PDFDraw tasks can begin execution
					doc.Unlock();

					// Wait for the PDFDraw tasks to complete.
					foreach (Task t in tasks)
					{
						t.Wait();
					}
				}
			}
			catch (PDFNetException e) 
			{
				Console.WriteLine(e.Message);
			}
			PDFNet.Terminate();
		}
	}
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/core/get-started/samples/multithreadedtest.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
