Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SerialQueue

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues.

Provided as a Nuget package built for Net462 and NetStandard20

What is a Serial Queue?

Apple's Grand Central Dispatch library provides three kinds of "operation queues".

An operation queue is a lightweight abstraction over a thread, and can be used for concurrency and thread safety

Concurrent, "Main" and Serial.

Neither Concurrent nor Main queues are neccessary in .NET as a concurrent queue is basically the same as Task.Run or ThreadPool.QueueUserWorkItem, and the Main queue is basically the same as Dispatcher.BeginInvoke (for WPF) or whatever the equivalent for your particular UI framework happens to be.

Serial queues on the other hand have no built-in equivalent. Apple's documentation describes them:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue. Serial queues are often used to synchronize access to a specific resource. You can create as many serial queues as you need, and each queue operates concurrently with respect to all other queues. In other words, if you create four serial queues, each queue executes only one task at a time but up to four tasks could still execute concurrently, one from each queue.

Why would I want to use one?

There are quite a few scenarios where one might like to use a thread to ensure thread-safety of some object (e.g. "All accesses to the private data of X object must be performed on thread Y"), however threads themselves are often too resource-intensive to be able to do that at a smaller scale (e.g. where you have thousands of such objects.)

To get around this in the past I have seen (and used) things like thread sharing, or fallen back to "full blown" concurrency where things just run randomly on a threadpool and locking must be used everywhere. Both of these options are more complex and dangerous, particularly full blown concurrency where all methods must all lock correctly.

Serial queues offer a very interesting option in the "middle" of these two spaces.

  • They are very lightweight (each queue is literally not much more than a List and a few lock objects), so you can easily have thousands of them
  • They have a usage model which is similar to threads and is easier to reason about

Example

varq=newSerialQueue();q.DispatchAsync(()=>{Console.WriteLine("a");});q.DispatchAsync(()=>{Console.WriteLine("b");});

In the above example, both operations are guaranteed to execute in-order and guaranteed not to execute at the same time. Thus, the actions are thread-safe and easy to reason about.

The actual execution of the functions is managed by the built-in .NET ThreadPool (the default implementation just uses Task.Run) so many thousands of queues will be backed by perhaps 8 or so underlying OS threads in the threadpool

Enhancements for .NET

This serial queue supports async/await - i.e. if you are running within the context of a serial queue, then it will be captured across Async/Await

varq=newSerialQueue();q.DispatchAsync(async()=>{// we are on the queuevarresponse=awaitSomeNetworkRequest();// we are still on the queue, it was captured by the await});

You can also await the queue itself directly in order to "jump" to it if you are in an existing async method

SerialQueuem_queue=newSerialQueue();// imagine this is a WPF or winforms apppublicvoidButton_Click(){// here we are on the UI main threadvarresult=awaitDoBackgroundProcessing();// and we are still on the UI thread because 'await DoBackgroundProcessing' captured the sync context.MyTextBox.Text=result;}privateasyncTask<string>DoBackgroundProcessing(){// at this point we are still on the UI main threadawaitm_queue;// now we are OFF the main UI thread and onto the serial queue (behind the scenes we're on a threadpool thread)varresponse=awaitSendNetworkRequest();// still on the serial queuereturnresponse;}

More Documentation is available on the Github wiki

About

C# Implementation of a SerialQueue in the style of Apple's Grand Central Dispatch queues

Resources

Stars

69 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages