A Private ReadOnly gate As New Object()
Private total As Integer
Public Sub Add(value As Integer)
SyncLock gate
total += value
End SyncLock
End SubAn asynchronous method should generally not wait synchronously on a task while holding a lock. Prefer Private ReadOnly gate As New SemaphoreSlim(1, 1)
Public Async Function UpdateAsync() As Task
Await gate.WaitAsync()
Try
Await SaveAsync()
Finally
gate.Release()
End Try
End Function3.3 Deadlock, starvation, and livelockA deadlock occurs when activities wait indefinitely for one another. A common lock-ordering deadlock arises when task A holds lock X and waits for Y, while task B holds Y and waits for X. Prevention techniques include consistent lock ordering, short critical sections, timeouts, and avoiding nested locks. Starvation occurs when an activity repeatedly fails to obtain a resource. Livelock occurs when activities remain active but make no useful progress, often because they repeatedly react to one another. These failures demonstrate that “thread-safe” does not necessarily mean “progress-safe.” 4. Parallel Computation in Visual Basic4.1 Task Parallel LibraryThe Task Parallel Library (TPL) provides tasks, parallel loops, task composition, and scheduling facilities. Parallel.For(0, values.Length,
Sub(i)
values(i) = ExpensiveFunction(values(i))
End Sub)The loop body must be safe under concurrent execution. It should not update a shared non-thread-safe collection or depend on iteration order unless suitable synchronization is added. PLINQ can express parallel query operations: Dim result = values.AsParallel().
Where(Function(x) x > 0).
Select(Function(x) Transform(x)).
ToArray()Parallel query is not automatically faster. Overhead, ordering requirements, partitioning, data size, and memory bandwidth influence the result. 4.2 Data parallelism and task parallelismData parallelism applies the same operation to independent elements of a data set. Task parallelism executes different operations concurrently, such as parsing, validation, and persistence stages. A pipeline combines stages and may use bounded buffers to prevent a fast producer from overwhelming a slow consumer. 4.3 Reduction and thread-local stateA parallel computation often follows a map-reduce structure: map each input independently, then combine partial results. For a sum, each worker can calculate a local subtotal, after which subtotals are combined. This reduces lock contention and improves locality. Floating-point reduction deserves special caution. Addition is mathematically associative but generally not exactly associative in finite-precision arithmetic. Different execution orders may therefore produce slightly different results. Reproducibility requirements should determine whether parallel floating-point reduction is acceptable. 5. Performance Models and Measurement5.1 Essential metricsLatency is the time required to complete one operation. Throughput is completed work per unit time. For a stable system, average throughput may be expressed as: Throughput = Completed operations ÷ Elapsed time If 12,000 requests complete in 60 seconds, throughput is 200 requests per second. This relationship assumes the measurement interval and completed-operation definition are clear. For a workload that can be divided into a fraction p that benefits from parallel execution and a fraction 1 − p that remains serial, Amdahl’s law estimates speedup with N processors: S(N) = 1 ÷ ((1 − p) + p ÷ N) Here, S(N) is speedup relative to one processor, p is the parallelizable fraction between 0 and 1, and N is the number of processors. The model assumes fixed workload, negligible overhead, and ideal load distribution; real systems usually achieve less. For p = 0.90 and N = 8: S(8) = 1 ÷ (0.10 + 0.90 ÷ 8) = 1 ÷ 0.2125 ≈ 4.71 Thus, eight processors do not imply an eightfold speedup. Parallel efficiency is: E(N) = S(N) ÷ N In this example, efficiency is approximately 4.71 ÷ 8 ≈ 0.589, or 58.9 percent. Gustafson’s law provides a complementary perspective for scaled workloads, where the problem size grows with available processors: SG(N) = N − α × (N − 1) Here α is the serial fraction of the scaled execution. Amdahl’s law is useful for fixed-size work; Gustafson’s law is useful when additional processors permit larger workloads. 5.2 Measurement methodologyPerformance engineering should follow a disciplined cycle:
Averages alone can hide poor user experience. Percentiles such as p95 and p99 describe tail latency: p99 is the value below which approximately 99 percent of measured requests fall. Benchmark results should report workload size, hardware, runtime version, configuration, and variance. 5.3 Common sources of overheadParallel execution introduces task scheduling, synchronization, partitioning, context switching, cache misses, allocation, and result-combination costs. If each unit of work is too small, overhead may exceed useful computation. Batching, thread-local accumulation, data locality, and appropriate task granularity can improve results. 6. Bounded Concurrency and PipelinesA realistic service often receives more work than it can safely process simultaneously. A semaphore can limit active operations: Dim limit As New SemaphoreSlim(8)
Dim tasks = items.Select(Async Function(item)
Await limit.WaitAsync()
Try
Return Await ProcessAsync(item)
Finally
limit.Release()
End Try
End Function).ToArray()
Dim results = Await Task.WhenAll(tasks)The number eight is a policy choice, not a universal optimum. It should be evaluated against remote service limits, processor count, memory, and observed latency. For continuous workloads, a producer-consumer pipeline is often superior to creating one task per item. A bounded channel or queue provides back-pressure: producers slow down when consumers cannot keep up. This protects memory and downstream systems. A robust pipeline defines:
Retries require special care. Retrying a non-idempotent operation can duplicate effects. Exponential back-off with jitter can reduce synchronized retry storms, but it must be combined with a maximum attempt count and a clear failure path. 7. Illustrative Case Study: Concurrent Document IngestionThe following is a fictional illustrative case designed to integrate the chapter’s concepts. BackgroundA university research repository receives batches of PDF documents. Each document must be downloaded, text-extracted, classified, and indexed. The original Visual Basic service processes documents sequentially. A batch of 10,000 documents takes too long, and the user interface becomes unresponsive when progress is displayed. Problem analysisProfiling reveals that downloading and indexing are I/O-bound, while text classification is CPU-bound. The original implementation also creates a database connection for every document and retries failed downloads immediately without a limit. A proposed solution of creating 10,000 threads is rejected because threads consume resources and do not solve remote-service throttling. ApproachThe redesigned system uses:
The download stage places completed content into a bounded buffer. The classification stage performs CPU-intensive work in parallel. The indexing stage batches database writes where the database API permits it. Each stage releases resources in OutcomeIn a controlled benchmark, throughput improves substantially without increasing the number of operating-system threads linearly with the document count. More importantly, memory remains bounded because the buffer has finite capacity. When the indexing database slows, back-pressure propagates to classification and downloading rather than allowing unlimited accumulation. The design does not assume that maximum concurrency is best. Increasing download concurrency beyond the service’s permitted rate causes throttling and raises tail latency. The final configuration is selected from measurements of throughput, p95 latency, error rate, CPU utilization, and memory use. Learning pointsThe case demonstrates that performance is an end-to-end property. Asynchronous I/O, parallel CPU work, bounded concurrency, idempotency, cancellation, and observability must be designed together. A local speedup is not a system improvement if it overloads a dependency or compromises correctness. 8. Best Practices, Governance, and Contemporary DirectionsUse asynchronous APIs throughout an I/O call chain; avoid blocking with Keep critical sections small, establish lock-ordering rules, and document ownership of mutable state. Prefer immutable data and message passing where practical. Make cancellation and timeout behavior explicit. Treat external operations as fallible and distinguish transient failures from permanent failures. Concurrency also has governance implications. A service that launches excessive requests can violate a provider’s acceptable-use policy. Personal or confidential data processed concurrently must remain protected in logs, temporary buffers, and diagnostic traces. Performance optimization must not weaken authorization, auditability, encryption, or data-retention controls. Reproducibility is important in scientific and machine-learning workloads, where nondeterministic scheduling can affect floating-point results or model-training outcomes. Contemporary .NET development increasingly combines task-based asynchronous programming with channels, dataflow-style pipelines, telemetry, and cloud-native scaling. Hardware heterogeneity, including CPUs with different core types and accelerators, makes empirical measurement more important. AI-assisted code generation may produce plausible asynchronous code, but generated code must be reviewed for unbounded concurrency, incorrect shared-state access, missing cancellation, exception loss, and privacy risks. Challenges and LimitationsAsynchronous code can be harder to debug because execution is separated across time and sometimes across contexts. Parallel failures may be nondeterministic and difficult to reproduce. Synchronization can eliminate races but introduce contention. More concurrency can increase throughput up to a point and then reduce it. Benchmark results can be distorted by warm-up effects, caching, garbage collection, background processes, unrealistic data, or insufficient repetitions. No performance model fully predicts a production system. Amdahl’s law abstracts away memory bandwidth, communication, scheduling, and contention; queueing behavior can make latency rise sharply as utilization approaches capacity. Therefore, analytical models should guide hypotheses, while production-like measurement validates decisions. Chapter SummaryAsynchronous programming allows operations to make progress without blocking the initiating thread and is particularly valuable for I/O-bound workloads. In Visual Basic, Correct concurrent programs require control of shared state, appropriate synchronization, avoidance of deadlock and starvation, and explicit handling of exceptions and cancellation. TPL facilities such as Performance must be measured using defined workloads and metrics such as throughput, latency, percentiles, speedup, and efficiency. Amdahl’s law illustrates why serial work limits fixed-size speedup, while real systems also incur scheduling, synchronization, memory, and communication overhead. Effective professional practice combines correctness, scalability, observability, security, governance, and empirical evaluation. Key TermsAsynchrony: Execution in which an operation can progress without blocking the initiating thread. Await: Visual Basic expression that asynchronously waits for a task and resumes when it completes. Concurrency: Overlapping progress of multiple logical activities. Parallelism: Simultaneous execution of activities on multiple processing resources. Task: .NET representation of an asynchronous operation or future completion. I/O-bound: Work whose duration is dominated by waiting for external input or output. CPU-bound: Work whose duration is dominated by computation. Race condition: Incorrect behavior caused by timing-dependent access to shared state. Thread safety: Property that concurrent use does not violate correctness guarantees. Deadlock: Indefinite waiting caused by circular resource dependencies. Starvation: Failure of an activity to obtain resources or execution opportunities. Semaphore: Synchronization primitive that limits simultaneous access. Cancellation token: Cooperative signal requesting that work stop. Back-pressure: Mechanism that limits production when downstream processing is slower. Throughput: Amount of completed work per unit time. Latency: Time required to complete one operation. Speedup: Ratio of baseline execution time to improved execution time. Amdahl’s law: Model relating speedup to parallelizable work and processor count. Idempotency: Property that repeating an operation has the same intended effect as performing it once. Critical section: Code region requiring protected access to shared state. Review QuestionsShort Answer Questions
Long Answer Questions
Analytical / Application Questions
Multiple Choice Questions with AnswersQuestion 1Which statement best describes the relationship between asynchronous execution and parallel execution? A. Asynchronous execution always creates a new operating-system thread. B. Asynchronous execution can overlap waiting without requiring simultaneous CPU execution. C. Parallel execution is possible only for I/O-bound operations. D. The two terms have exactly the same meaning. Correct Answer: B. Asynchronous execution can overlap waiting without requiring simultaneous CPU execution. Question 2Which return type is generally appropriate for a reusable Visual Basic asynchronous method that produces no value? A. Integer B. Thread C. Task D. Async Sub Correct Answer: C. Task Question 3What is the principal purpose of a CancellationToken? A. To forcibly terminate any thread immediately B. To request cooperative cancellation of an operation C. To guarantee that an operation succeeds D. To increase the processor clock frequency Correct Answer: B. To request cooperative cancellation of an operation Question 4Which synchronization primitive is especially suitable for limiting the number of simultaneous asynchronous operations? A. SemaphoreSlim B. StringBuilder C. StreamReader D. Random Correct Answer: A. SemaphoreSlim Question 5Under Amdahl’s law, what limits the speedup of a fixed-size workload? A. The serial fraction of the workload B. The number of variable names C. The screen resolution D. The number of comments in the code Correct Answer: A. The serial fraction of the workload Question 6What problem does back-pressure primarily address in a producer-consumer pipeline? A. It increases the number of duplicate messages. B. It prevents a fast producer from overwhelming downstream stages. C. It removes the need for error handling. D. It guarantees zero latency. Correct Answer: B. It prevents a fast producer from overwhelming downstream stages. Question 7Why can parallel floating-point reduction produce slightly different results from sequential reduction? A. Floating-point addition is not necessarily exactly associative. B. Processors cannot add floating-point values in parallel. C. Parallel loops always change the input values. D. The compiler converts all numbers to strings. Correct Answer: A. Floating-point addition is not necessarily exactly associative. Question 8Which practice is most likely to cause thread starvation or deadlock in context-sensitive applications? A. Awaiting an asynchronous API B. Using immutable local variables C. Blocking synchronously on a task with D. Passing a cancellation token to an API Correct Answer: C. Blocking synchronously on a task with Question 9A workload completes 12,000 operations in 60 seconds. What is its throughput? A. 20 operations per second B. 120 operations per second C. 200 operations per second D. 720,000 operations per second Correct Answer: C. 200 operations per second Question 10Which property is especially important when retrying an operation that may be executed more than once? A. Idempotency B. Random screen placement C. UI thread affinity D. Large lock duration Correct Answer: A. Idempotency
Programming in Visual Basic
|
Chapter 9 of 9 |