European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core 9.0 Hosting - HostForLIFE :: Task: Synchronous vs. Asynchronous.Task and WaitAll.WhenEverything in.NET

clock July 9, 2024 07:09 by author Peter

Asynchronous programming in C# frequently entails doing several tasks at once. Task and Split are two popular approaches to managing many tasks.Task.WhenAll and WaitAll. Despite their apparent similarities, they have different functions and are employed in various contexts. The distinctions between Task.WaitAll and Task.WhenAll are examined in this article, along with real-world examples to show how to use both.


What is Task.WaitAll?
Assignment.The synchronous function WaitAll stops the calling thread after each of the supplied tasks is finished. It's helpful when you have to make sure that a group of tasks is completed before moving on, but it does so in a blocking way, meaning that Task is called by the thread.Until every task is completed, WaitAll is in use.

Example Usage of Task.WaitAll
using System;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {
        Task task1 = Task.Run(() => PerformTask(1));
        Task task2 = Task.Run(() => PerformTask(2));
        Task task3 = Task.Run(() => PerformTask(3));
        Task.WaitAll(task1, task2, task3); // Blocks until all tasks complete
        Console.WriteLine("All tasks completed.");
    }
    static void PerformTask(int taskId)
    {
        Console.WriteLine($"Task {taskId} starting.");
        Task.Delay(1000).Wait(); // Simulate work
        Console.WriteLine($"Task {taskId} completed.");
    }
}


In this example, Task.WaitAll blocks the main thread until all three tasks are complete.

What is Task.WhenAll?

Task.WhenAll is an asynchronous method that returns a single task that is completed when all the provided tasks have been completed. Unlike Task.WaitAll, it does not block the calling thread. Instead, it allows the calling code to continue executing asynchronously.

Example Usage of Task.WhenAll

using System;
using System.Threading.Tasks;
class Program
{
    static async Task Main()
    {
        Task task1 = Task.Run(() => PerformTask(1));
        Task task2 = Task.Run(() => PerformTask(2));
        Task task3 = Task.Run(() => PerformTask(3));
        await Task.WhenAll(task1, task2, task3); // Waits for all tasks to complete asynchronously
        Console.WriteLine("All tasks completed.");
    }
    static void PerformTask(int taskId)
    {
        Console.WriteLine($"Task {taskId} starting.");
        Task.Delay(1000).Wait(); // Simulate work
        Console.WriteLine($"Task {taskId} completed.");
    }
}


In this example, Task.WhenAll allows the main method to await the completion of all tasks without blocking the calling thread.

Key Differences
Blocking vs. Non-blocking

  • Task.WaitAll: Blocks the calling thread until all tasks are complete.
  • Task.WhenAll: Returns a task that can be awaited, allowing the calling thread to continue execution asynchronously.

Return Type

  • Task.WaitAll: Does not return a value.
  • Task.WhenAll: Returns a Task that represents the completion of all provided tasks.

Usage Scenario

  • Task.WaitAll: Used when you need to block until tasks are complete, typically in non-UI or console applications.
  • Task.WhenAll: Used in asynchronous programming, especially in UI applications where blocking the main thread is undesirable.

Practical Use Cases
When to Use Task.WaitAll

  • In console applications where you need to ensure that certain tasks are completed before moving on.
  • When you are dealing with legacy code that doesn’t support async/await patterns.

When to Use Task.WhenAll

  • In UI applications keep the interface responsive.
  • In web applications handle multiple asynchronous operations without blocking the main thread.

Conclusion
Task.WaitAll and Task.WhenAll are essential tools in C# for handling multiple tasks. Use Task.WaitAll when you need to block the calling thread until tasks are complete, and Task.WhenAll for asynchronous waiting. Understanding their differences and appropriate use cases can help you write more efficient and responsive applications.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Resilience in a.NET 8 Web API Implementation

clock July 2, 2024 08:16 by author Peter

Creating robust APIs is more important than ever in the age of modern web development, especially with all these third-party integration we rely on. In this Blog, We will be guiding you how to introduce resilience in. Create an HTTP Client in a. Extensions. Http. Resilience library. In this section, we will take a look at features like setting up retry policies with exponential backoff and timeouts to make your API more resilient against the transient faults.

Step 1. Create a new. NET 8 Web API project Step First, if you have no existing Project then create new with. NET CLI dotnet new web or starting with the default Web API template.

Step 2. Install the Microsoft.AspNetCore. Extensions. Http. Resilience library via NuGet:
dotnet add package Microsoft.Extensions.Http.Resilience --version 8.0.0

Step 3. Configure Resilience in Program.cs
Modify the Program.cs file to set up HttpClient with resilience policies provided by Microsoft.Extensions.Http.Resilience. Here, we will define retry policies and timeouts.
//Add resilience pipeline
builder.Services.AddResiliencePipeline("default", x =>
{
    x.AddRetry(new Polly.Retry.RetryStrategyOptions
    {
        ShouldHandle = new PredicateBuilder().Handle<Exception>(),
        Delay = TimeSpan.FromSeconds(2),
        MaxRetryAttempts = 2,
        BackoffType = DelayBackoffType.Exponential,
        UseJitter = true
    })
    .AddTimeout(TimeSpan.FromSeconds(30));
});

Step 4. Use the Resilient HttpClient in a Service
Next, we'll inject and use the configured HttpClient in your Service. This example shows how to fetch data from an external API using the resilient HttpClient.
public class WeatherService
{
    private readonly HttpClient _httpClient;
    private readonly ResiliencePipelineProvider<string> _resiliencePipelineProvider;
    public WeatherService(HttpClient httpClient,
                         ResiliencePipelineProvider<string> resiliencePipelineProvider)
    {
        _httpClient = httpClient;
        _resiliencePipelineProvider = resiliencePipelineProvider;

    }
    public async Task<string> GetWeatherAsync()
    {
        var pipeline = _resiliencePipelineProvider.GetPipeline("default");
        var response = await pipeline
            .ExecuteAsync( async ct=> await _httpClient.GetAsync($"https://localhost:7187/weatherforecast",ct));

        return await response.Content.ReadAsStringAsync();
    }

}


Step 5. Add the endpoint in the Program.cs
app.MapGet("/weatherService/weather", async (WeatherService weatherService) =>
{
    var result = await weatherService.GetWeatherAsync();
    return result;
})
    .WithName("GetWeather")
    .WithOpenApi();


Step 6. Run the Application
Finally, run your application and navigate to the endpoint to see the resilient HttpClient in action.
References

Please refer to the below links for more details.

  • Building resilient cloud services with .NET 8
  • Learning from Microsoft

Conclusion
By following these steps, you have integrated resilience into your .NET 8 Web API project using Microsoft.Extensions.Http.Resilience library. The retry policies, circuit breaker settings, and timeouts will help ensure your API is robust against transient faults, improving its reliability and user experience.

 



European ASP.NET Core 9.0 Hosting - HostForLIFE :: Caching Strategies in Angular and .NET Core

clock June 25, 2024 08:07 by author Peter

One essential tactic for enhancing the scalability and performance of web applications is caching. Caching can be done in a variety of methods inside the frameworks of Angular (a client-side framework) and.NET Core (a server-side framework), each appropriate for a particular set of circumstances. The following are some typical caching techniques for both:

Angular Caching
Caching HTTP Requests with an HTTP Interceptor

  • To cache HTTP responses, use an HTTP interceptor. By delivering cached responses when appropriate, this can assist lower the amount of HTTP queries made to the server.
  • For instance. Construct an HTTP interceptor that looks through the cache before sending a network request, and a caching service that saves responses.
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
  private cache = new Map<string, any>();
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.method !== 'GET') {
      return next.handle(req);
    }
    const cachedResponse = this.cache.get(req.url);
    if (cachedResponse) {
      return of(cachedResponse);
    }
    return next.handle(req).pipe(
      tap(event => {
        this.cache.set(req.url, event);
      })
    );
  }
}


Service Worker for Offline Caching
  • Use Angular's built-in service worker support to cache assets and API responses for offline access and faster load times.
  • Example. Enable the Angular service worker by running ng add @angular/pwa and configure caching in the ngsw-config.json file.
Local Storage or IndexedDB
Store data in the browser's local storage or IndexedDB for long-term caching. This can be useful for storing user preferences or large datasets that rarely change.
Example. Use Angular's localStorage or IndexedDB wrappers to store and retrieve data.
localStorage.setItem('key', JSON.stringify(data));
const cachedData = JSON.parse(localStorage.getItem('key'));

Caching in .NET Core
In-Memory Caching
Store cache data in the memory of the web server. This is useful for small amounts of data that change frequently.
Example. Use the IMemoryCache interface to cache data in memory.
public class MyService
{
    private readonly IMemoryCache _memoryCache;
    public MyService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }
    public string GetData()
    {
        string cacheKey = "myCacheKey";
        if (!_memoryCache.TryGetValue(cacheKey, out string cachedData))
        {
            cachedData = "Data from database";
            _memoryCache.Set(cacheKey, cachedData, TimeSpan.FromMinutes(5));
        }
        return cachedData;
    }
}


Distributed Caching
  • Store cache data in a distributed cache like Redis or SQL Server. This is useful for data that needs to be shared across multiple servers.
  • Example. Use the IDistributedCache interface to cache data in Redis.
public class MyService
    {
        private readonly IDistributedCache _distributedCache;
        public MyService(IDistributedCache distributedCache)
        {
            _distributedCache = distributedCache;
        }
        public async Task<string> GetDataAsync()
        {
            string cacheKey = "myCacheKey";
            var cachedData = await _distributedCache.GetStringAsync(cacheKey);
            if (cachedData == null)
            {
                cachedData = "Data from database";
                await _distributedCache.SetStringAsync(cacheKey, cachedData, new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
                });
            }
            return cachedData;
        }
    }

Response Caching Middleware
  • Cache HTTP responses at the server level to improve performance for subsequent requests.
  • Example. Configure response caching middleware in the Startup class.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCaching();
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCaching();
        app.Use(async (context, next) =>
        {
            context.Response.GetTypedHeaders().CacheControl =
                new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
                {
                    Public = true,
                    MaxAge = TimeSpan.FromSeconds(60)
                };
            await next();
        });
    }


Combining Angular and .NET Core Caching

To achieve optimal performance, you can combine caching strategies in Angular and .NET Core:

Client-Side Caching

Cache static assets and API responses in Angular using service workers or local storage.
Implement HTTP interceptors to cache responses and reduce server requests.

Server-Side Caching
Use in-memory or distributed caching in .NET Core to store frequently accessed data.
Implement response caching middleware to cache entire HTTP responses.

Conclusion
You can build a strong and effective caching strategy that greatly improves the efficiency and scalability of your Angular and.NET Core applications by utilizing both client-side and server-side caching.



European ASP.NET Core 9.0 Hosting - HostForLIFE :: How to Exploring the Experimental Attribute in C#?

clock June 20, 2024 13:52 by author Peter

In software development, the introduction of new features often involves a period of testing and feedback. During this period, developers need a way to indicate that certain features are not fully mature and may be subject to change. The Experimental attribute in C# serves this purpose by marking classes, methods, or other members as experimental. This blog will delve into what the Experimental attribute is, why it's useful, how to implement it, and provide detailed code snippets to illustrate its usage.


What is the Experimental Attribute?

The Experimental attribute is a custom attribute that can be applied to various members of your codebase (e.g., classes, methods, properties). It indicates that the marked feature is experimental, meaning it's in a testing phase and may undergo significant changes or might even be removed in future releases.
Why Do We Need It?

  • Communication: It communicates to other developers that the marked feature is not yet stable and should be used with caution.
  • Encapsulation: It helps encapsulate features that are still under development, making it easier to manage their lifecycle.
  • Documentation: It serves as a form of documentation, indicating the experimental nature directly in the code.


Implementing the Experimental Attribute
Step 1. Define the Experimental Attribute
First, you need to define the Experimental attribute. This is a simple custom attribute class.
using System;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)]
public class ExperimentalAttribute : Attribute
{
    public string Message { get; }
    public ExperimentalAttribute(string message)
    {
        Message = message;
    }
}


Step 2. Applying the Experimental Attribute
You can apply the Experimental attribute to various parts of your codebase to indicate that they are experimental.
using System;
[Experimental("This class is experimental and may change in the future.")]
public class ExperimentalFeature
{
    [Experimental("This method is experimental and may change in the future.")]
    public void ExperimentalMethod()
    {
        Console.WriteLine("This is an experimental method.");
    }
    [Experimental("This property is experimental and may change in the future.")]
    public string ExperimentalProperty { get; set; }
}

Step 3. Using the Experimental Features
When using the experimental features, it’s important to handle them with care, knowing that their behavior might change.
public class Program
{
    public static void Main()
    {
        ExperimentalFeature feature = new ExperimentalFeature();
        feature.ExperimentalMethod();
        feature.ExperimentalProperty = "Testing";
        Console.WriteLine(feature.ExperimentalProperty);
    }
}


Detailed Explanation of Code Snippets
Defining the Attribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)]
public class ExperimentalAttribute : Attribute
{
    public string Message { get; }
    public ExperimentalAttribute(string message)
    {
        Message = message;
    }
}

  • [AttributeUsage]: Specifies the program elements to which the attribute can be applied. Here, it can be applied to classes, methods, and properties.
  • Constructor: Initializes the attribute with a message that provides additional context or warnings about the experimental feature.

Applying the Attribute
[Experimental("This class is experimental and may change in the future.")]
public class ExperimentalFeature
{
    [Experimental("This method is experimental and may change in the future.")]
    public void ExperimentalMethod()
    {
        Console.WriteLine("This is an experimental method.");
    }
    [Experimental("This property is experimental and may change in the future.")]
    public string ExperimentalProperty { get; set; }
}

  • Class Level: Marks the entire class as experimental.
  • Method Level: Marks a specific method within the class as experimental.
  • Property Level: Marks a property within the class as experimental.

Using the Experimental Features
public class Program
{
    public static void Main()
    {
        ExperimentalFeature feature = new ExperimentalFeature();
        feature.ExperimentalMethod();
        feature.ExperimentalProperty = "Testing";
        Console.WriteLine(feature.ExperimentalProperty);
    }
}


Creating an Instance: An instance of the ExperimentalFeature class is created and its experimental method and property are used. This is straightforward, but developers should be aware of the experimental status.

Conclusion

The Experimental attribute in C# is a powerful tool for marking features as experimental, providing clear communication to other developers about the stability and maturity of the code. By defining and applying this custom attribute, you can better manage the development and evolution of new features, ensuring that everyone involved in the project is aware of their experimental nature.

Using the Experimental attribute helps in documenting and encapsulating the experimental features effectively, making your codebase more maintainable and understandable. While the examples provided are simple, the approach can be scaled to larger and more complex projects, enhancing the overall development process.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: How to Frozen Collections in .NET 8?

clock June 11, 2024 10:38 by author Peter

With the release of .NET 8, Microsoft has introduced several new features and improvements to enhance performance and developer productivity. Among these, one of the most notable additions is the concept of "Frozen Collections." This article explores what frozen collections are, their benefits, and how to use them effectively in your .NET applications.

Frozen Collections: What Are They?
A new kind of immutable collection called frozen collections is intended to improve access speed and reduce memory usage in order to maximize performance. Frozen collections are intentionally made to be immutable from the beginning, in contrast to conventional read-only collections, which are frequently nested inside changeable collections. Once a collection is frozen, its immutability ensures that it cannot be altered, which has various performance benefits.

Key Benefits of Frozen Collections
1. Performance Optimization
The primary advantage of frozen collections lies in their performance. By guaranteeing immutability, the .NET runtime can make several optimizations.

  • Memory Efficiency: Since frozen collections do not change, they can be stored in a compact form, reducing the overall memory footprint.
  • Access Speed: Lookup operations are faster because the underlying data structure can be optimized for read access.
  • Thread Safety: Frozen collections are inherently thread-safe, eliminating the need for synchronization mechanisms when accessing them concurrently.


2. Predictable Behavior
Immutable collections provide predictable behavior, which simplifies debugging and reasoning about code. When a collection is guaranteed not to change, developers can avoid common pitfalls related to concurrent modifications and side effects.

3. Simplified Code

Using frozen collections can lead to cleaner and more maintainable code. Since the collections cannot be altered, functions that operate on them do not need to account for modifications, leading to simpler and more readable implementations.

Using Frozen Collections in .NET 8
To use frozen collections in .NET 8, you can leverage the new FrozenCollection<T> class available in the System.Collections.Immutable namespace. Here’s a basic example to illustrate how to create and use a frozen collection.
using System;
using System.Collections.Immutable;

class Program
{
    static void Main()
    {
        var mutableList = new List<int> { 1, 2, 3, 4, 5 };

        // Create a frozen collection from the mutable list
        var frozenCollection = mutableList.ToFrozenCollection();

        // Attempting to modify the frozen collection will result in a compile-time error
        // frozenCollection.Add(6); // Not allowed

        // Accessing elements is fast and efficient
        Console.WriteLine(frozenCollection[0]); // Output: 1

        // The collection is inherently thread-safe
        Parallel.ForEach(frozenCollection, item => {
            Console.WriteLine(item);
        });
    }
}

public static class Extensions
{
    public static FrozenCollection<T> ToFrozenCollection<T>(this IEnumerable<T> source)
    {
        var builder = new FrozenCollectionBuilder<T>();
        foreach (var item in source)
        {
            builder.Add(item);
        }
        return builder.ToFrozenCollection();
    }
}

Creating a Frozen Collection
In the example above, we start with a mutable list of integers. Using an extension method, we convert this list into a FrozenCollection<T>. The FrozenCollectionBuilder<T> class is used to add elements to the collection before it is frozen.

Accessing Elements

Once the collection is frozen, we can access its elements just like a regular collection. However, any attempt to modify the collection will result in a compile-time error, ensuring immutability.

Thread Safety

The inherent thread safety of frozen collections allows for safe concurrent access without additional synchronization, making them ideal for use in multi-threaded environments.

Conclusion

Frozen collections in .NET 8 represent a significant advancement in collection management, providing developers with powerful tools to create high-performance, immutable data structures. By leveraging these collections, you can achieve better memory efficiency, faster access times, and simplified code. As .NET continues to evolve, features like frozen collections will play a crucial role in enabling developers to build robust and efficient applications.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Increasing Code Quality: The Best Methods for Successful Code Reviews

clock June 3, 2024 07:40 by author Peter

Clarify goals and expectations prior to starting a code review. Make sure that the review's goal is understood, whether it is to verify code quality, find errors, impart knowledge, or uphold coding standards. Establishing specific goals guarantees that reviewers concentrate on the most pertinent parts of the code and speeds up the review process.

Specify the codes
Specify recommended practices and coding standards that all developers should follow. This covers standards for code organization, formatting rules, naming conventions, and documentation. Adhering to coding standards consistently makes the codebase easier to read, maintain, and scale, which in turn makes it easier for developers to communicate and work together on code.

Conduct regular reviews
Regularly incorporate code reviews into the development process. Establish systems that enable peer reviews for each code update, such as pull requests or code review software. Frequent reviews guarantee that code quality is maintained at a high level throughout the development process, assist find problems early, and prevent the building of technical debt.

Keep reviews small and focused

Divide code modifications into more digestible, smaller pieces to enable targeted and effective assessments. Big code changes are frequently intimidating and prone to problems that go unnoticed. Smaller, more targeted evaluations allow reviewers to give more insightful criticism and developers to handle issues more skillfully.

Foster a collaborative culture

Encourage a cooperative and positive atmosphere when conducting code reviews. Urge reviewers to focus on the code rather than the developer and to offer constructive criticism in a courteous manner. Promote transparent communication, stimulate dialogue, and take use of evaluations as a means of imparting information and providing mentorship.

Automate where possible
Automate repetitive tasks and checks to streamline the code review process. Use automated tools for static code analysis, code formatting, and unit testing to identify common issues and enforce coding standards automatically. Automation reduces manual effort, improves consistency, and frees up time for reviewers to focus on higher-level concerns.

Prioritize feedback and follow-up

Prioritize feedback based on its severity, impact, and urgency. Address critical issues and blockers promptly to prevent delays in the development process. Keep track of feedback and action items, follow up on unresolved issues, and ensure that all feedback is addressed satisfactorily before closing the review.

Encourage learning and growth
View code reviews as opportunities for learning and professional growth. Encourage developers to seek feedback, ask questions, and engage in discussions to deepen their understanding of coding principles and best practices. Provide constructive feedback, mentorship, and guidance to help developers improve their skills and grow as professionals.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Using and Exercising the String Builder in.NET C#

clock May 27, 2024 07:04 by author Peter

Once a string type is generated in C#, its contents cannot be changed since the string type is immutable. This suggests that modifying a string-type object after it has been created will cause the object to be constructed again in memory as a new instance. Additionally, changing the string frequently could cause performance problems.


StringBuilder solves this problem because, unlike string, it can dynamically increase its memory to support any kind of manipulation.

How to make a StringBuilder correctly?
It is not too difficult to instantiate a StringBuilder. We can instantiate a class by using the new keyword to create an instance of that class. A StringBuilder also contains other constructors.

Conversion of StringBuilder to String
We can use a StringBuilder for any type of string manipulation. But the StringBuilder does not return a string. Therefore, in order to retrieve a string, we must use the ToString() method.
var sb = new StringBuilder("Peter Scott");

var name = sb.ToString();

The content is first converted to a string and then a StringBuilder is created with a default text.

StringBuilder Techniques

We can work with a StringBuilder's contents in a few different ways. These include Replace(), Insert(), Clean(), Remove(), Append(), AppendLine(), and AppendFormat().

Append
A new string is appended to the end of the existing StringBuilder using the Append() function. The StringBuilder's length can be doubled, and space allocation happens automatically.
var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
var name = sb.ToString();

Console.WriteLine("{0} - {1}", nameof(name), name);

AppendLine
AppendLine() is useful when we want to add a line terminator to our StringBuilder. To accomplish this, we can make a StringBuilder and use the following method.


var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
sb.AppendLine();
sb.AppendLine("How are you?");

Console.WriteLine(sb.ToString());

AppendFormat
AppendFormat() appends a string to the end of StringBuilder in a predetermined format. The conventions of either the chosen culture or the current system culture are revealed in the produced string. The method lets us pass the desired format for our string as an input.
var sb = new StringBuilder("Hello, ");
sb.Append("Peter Scott");
var text = "C# Corner Rank";
var rank = 120;
sb.AppendLine();
sb.AppendFormat("{0} - {1}", text, rank);

Console.WriteLine(sb.ToString());


This method adds, to our StringBuilder object, a string, substring, character array, part of a character array, or the string representation of a primitive data type at a certain place. We may also add the index for our insertion using this method.
var sb = new StringBuilder("Hello");
var name = ", Peter Scott";
sb.Insert(5, name);

Console.WriteLine(sb.ToString());


Replace
Several character occurrences in the StringBuilder object are replaced by this method. The desired character sequence and a new value are entered into the method as inputs.
var sb = new StringBuilder("Hello");
var name = ", Peter Scott";
sb.Insert(5, name);
Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Replace("Lee", "Cooper");

Console.WriteLine("Replace data: {0}", sb.ToString());


Replace
A predetermined amount of characters are removed from the StringBuilder using the Remove() function. The start index and the number of characters to be deleted are the input parameters.

var sb = new StringBuilder("Hello");
sb.Append(", Peter Scott");

Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Remove(0, 7);

Console.WriteLine("Remove data: {0}", sb.ToString());


Remove
The Remove technique eliminates 7 characters starting at index 0.

Clear

The StringBuilder object's characters are all eliminated using this function. This method reduces the length of a StringBuilder to zero.
var sb = new StringBuilder("Hello");
sb.Append(", Peter Scott");

Console.WriteLine("Orioginal data: {0}", sb.ToString());
Console.WriteLine();

sb.Clear();

Console.WriteLine("Clear data: {0}", sb.ToString());


Clear
We learned the new technique and evolved together.
Happy coding!



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: The Issue of Thread Synchronization and It's ResolutionAn Alternative View on Stack Memory in.NET

clock May 20, 2024 09:40 by author Peter

We shall discuss the Stack memory in.NET in this article. The portion of memory in.NET that is assigned to a thread is called the Stack. To put it simply, each thread has a stack—that is, its own memory—that is the last in, first out. Note: Because there is a connection between Stack memory and Heap memory, some parts of the article are explained but do not include Heap memory.


Memory Stack
Computer memory architecture is where the idea of a memory stack first appeared. Its essence also affects the various programming languages. After all, in any computer system, a programming language is a form of abstraction or communication. Many programming languages are forced to adopt it as a result.
So, even the CPU has push-and-pop instructions for providing thread (stack) execution and other things. Well, without further confusion, let’s continue our look at .NET and their use of Stack memory.

Nearly all developers are aware that a data structure is referred to as a "stack." There are two general strategies for the stack data structure: FIFO and LIFO. We employ the LIFO (last in first out) method to operate our stack memory. Moreover, distinguish between stack memory and the stack data structure. Because stack memory makes use of the stack data structure technology, names are the same. There are two different kinds of memory in.NET; a heap and a stack, both of which are found in RAM. Method calls, return addresses, arguments/parameters, and local variables are all tracked by the stack. However, reference-type objects are stored in Heap (this is a topic for another article).

To put it simply, every thread has a memory location of its own. This is the stack, which represents each thread's local memory. Thus, during method execution, threads define a local variable, retrieve the result, and then call another method, and so on. It is these actions that require stack memory. Furthermore take note that just one thread of execution uses the stack memory. Conversely, a heap is shared memory used by multiple threads, the extent of which is specified by the operating system process.

Allocation of Stacks
Stack allocation, to put it simply, is a memory allocation technique that makes use of the stack, a section of memory. The most recently allocated memory is the first to be deallocated because, as I previously explained, the stack uses the LIFO (last in first out) method of operation. There are two types in.NET. A reference type is one, and a value type is the other. Thus, the value of the type is saved immediately when allocating a value type in a stack. However, when dealing with reference types that reside in heaps, it stores the heap's reference address, also known as the object's pointer. Generally speaking, it means that a stack only contains values (a reference address is a type of value, but we'll talk about that in another article).

The figure above illustrates how two declared variables are present in a separate stack frame within the thread stack: a flag (int32, value type) and a pInst, instance of the Person class. We won't talk about the heap, but the picture demonstrates that it has the flag's value of 1, as well as the reference address of pInst. The address for the stack that refers the heap object—an object with data and methods stored on the heap—is what is meant to be used in the reference type. In .NET, Stack memory has a fixed size which a 1-MB stack is allocated when a thread is created. This size is predefined once the program starts, providing a consistent amount of stack memory available for function calls and local variable storage. However, the fixed nature of the stack means that it can only hold a limited amount of data. In addition, the speed of stack memory operations is a key advantage based on the simplicity of the stack’s push and pop mechanisms.

So, almost everyone wonders why the Stack memory is fast.

  • Firstly, stack memory uses static memory allocation, meaning that memory is given and taken away in a predictable way. This removes the need for complicated memory management, making operations quick and efficient.
  • Secondly, as I said above the stack has a fixed size, which avoids the overhead of finding available memory space, unlike heap memory. The simple push-and-pop operations also make it faster. These operations just adjust the stack pointer, which is very fast compared to the more complex operations needed for heap memory.
  • Thirdly, modern CPUs have special instructions for stack operations, like pushing and popping data, which are made to be very fast. The CPU also uses a dedicated register, the stack pointer, to efficiently manage the stack’s top position. In addition, stack memory often gets loaded into the CPU cache. This happens because stack data is frequently accessed in a straight line, making it more friendly to the cache. In contrast, accessing heap memory is indirect and can involve more cache misses, making it slower. This direct hardware support and cache efficiency ensure that stack operations have very little overhead.

Stack Frame/Call Stack

A stack frame, also known as an activation record, is a section of the stack memory allocated for a single function/method call. When a function/method is called, a new stack frame is allocated, and when the function/method returns, its stack frame is deallocated.

Basically, a stack frame contains.

  • Function/Method Arguments: The values passed to the function/method.
  • Local Variables: Variables that are declared inside the function/method.
  • Return Address: The location in the code to return to after the function/method completes.


Let’s start the explanation with a code example.
public void Calculate()
{
    int a = 1;
    int b = 10;
    int c = a + b;

    Write(c);
    Console.WriteLine("Finished");
}

public void Write(int number)
{
    Console.WriteLine(number);
}


As you can see, there is a method named Calculate that declares 3 variables and is also called the Write method it.

Assume that, a thread calls the Calculate method, how would this be visualized?

As you can see from the image above, the first stack frame belongs to the Calculate method which stores 3 variables, one of which is the result of the sum of two variables. The second stack frame belongs to the Write method which stores the argument and return address (line 9 just for understanding, in fact, it contains different values).

So when the Write method completes the execution, the second stack frame is deallocated and the thread returns to the first stack frame that belongs to the Calculate method, through the return address it will execute from that address line and complete the execution of the Calculate method. In this way, the call stack loop that was called in this thread will continue to execute (in our case, execution is completed).

StackOverflowException

As I mentioned before, the stack size is limited, and for this reason, when any function/method is called in an unlimited and nested way, a “StackOverflowException” occurs.

Assume that we have some function/method that is using recursion but there is not any limitation. It means the method has some calculation in which there is no return state. So, in this case, we will get “StackOverFlowException”.

Let’s take a look at a code example.

public class Program
{
    public static void Main()
    {
        Calculate(2, 3);
    }

    public static void Calculate(int a, int b)
    {
        int c = a + b;
        Calculate(c, c * c);
    }
}


When you execute this code, you will get “StackOverFlowException” because recursion provides the call itself and there is no return state or limit in this function/method. With this technique, every call to the function/method creates new stack frames and when the stack limit is exceeded, we get a “StackOverFlowException”.

If you would like to learn more, stay tuned!



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: The Issue of Thread Synchronization and It's Resolution

clock May 14, 2024 06:53 by author Peter

Overview of synchronization issues

  • Assume that our shared count variable has a starting value of 0.
  • We wish to use this variable for two simultaneous activities.

Using Thread 1, add 1 to 100 to this variable.
Using Thread 2, subtract 1 to 100 from this value.

  • Print the variable's count at the end. It should ideally print 0. but it won't Because working on the same variable simultaneously by many threads can have unexpected outcomes.

When does the synchronization problem happen?
Critical Section
When more than one threads try to access the same code segment that segment is known as the critical section.
So, when more than one thread is there in the critical section at the same time, it can lead to unexpected results and synchronization problem.

Race Condition
If more than one thread tries to enter inside the critical section at the same time then it might lead to the synchronization problem.

Preemption
Preemption is the ability of the operating system to preempt(that is stop or pause) a currently scheduled task in favor of a higher priority task.
A program that is inside the critical section and CPU preempts then it can lead to the synchronization problem.

Solutions to the synchronization problem

In C#, there are several ways to synchronize access to shared resources to ensure thread safety and prevent race conditions.

Using lock keyword
The lock keyword provides a convenient way to create a synchronized block of code. It internally uses the Monitor class to achieve synchronization. The lock keyword ensures that only one thread can execute the locked code block at a time.

Example
private static object syncObject = new object();
private void Increment()
{
lock(syncObject)
{
    // Critical section: Access shared resource
}
}


Using Monitor Class
Instead of using the lock keyword, we can directly use methods of the Monitor class for synchronization.

Example
private static object syncObject = new object();
private void Increment()
{
Monitor.Enter(syncObject);
try
{
    // Critical section: Access shared resource
}
finally
{
    Monitor.Exit(syncObject);
}
}


Using Mutex
A mutex is a synchronization primitive that allows only one thread to acquire it at a time. It's typically used for inter-process synchronization to synchronize threads within the same process.

Example
private static Mutex mutex = new Mutex();
private void Increment()
{
mutex.WaitOne();
try
{
    // Critical section: Access shared resource
}
finally
{
    mutex.ReleaseMutex();
}
}

Using Semaphore
A semaphore is a synchronization primitive that allows a specified number of threads to enter a critical section simultaneously. It's useful when we want to limit the number of threads accessing a resource.

Example
private static Semaphore semaphore = new Semaphore(1, 1); // Limits access to one thread
private void Increment()
{
semaphore.WaitOne();
try
{
    // Critical section: Access shared resource
}
finally
{
    semaphore.Release();
}
}


Using Interlocked Class
The Interlocked class provides atomic operations for variables that are shared between threads. It's useful for performing simple operations like incrementing a counter without the need for locking.

Example
private int counter = 0;
public void Increment()
{
Interlocked.Increment(ref counter);
}

Properties of a good solution to the synchronization problem

Mutual Exclusion: Only one thread should be allowed inside the critical section at any point in time.
Progress: Overall system should keep on making progress. There shouldn't be a deadlock condition.
Bounded waiting: No thread should wait outside the critical section infinitely. There should be some bound on the waiting time.
No Busy Waiting: If a thread has to continuously check if they can enter inside the critical section or not is Busy Waiting.
    while(!allowed to enter critical section)

    {
        checking(); // <---- This is the busy waiting.
    }


There shouldn't be Busy waiting as it can have several consequences like

  • Inefficient use of CPU resources and wasted energy.
  • Reduced performance.
  • Increased power consumption.
  • Potential deadlocks and etc.



European ASP.NET Core 8.0.1 Hosting - HostForLIFE :: Comprehending.NET Core Garbage Collection

clock May 6, 2024 08:07 by author Peter

A key component of memory management in contemporary programming languages like C# is garbage collection (GC). The GC system is essential to.NET Core because it automatically recovers memory that is no longer in use, eliminating memory leaks and guaranteeing effective memory use. The purpose of this article is to examine the techniques and the parts that make up the garbage collection system in .NET Core.

Comprehending Trash Collection
Effective memory management is essential for developing strong applications, especially with C# and.NET Core. Garbage Collection (GC) is a key component of this ecosystem's automated memory management system, effectively managing memory deallocation and allocation. The process of automatically recovering memory used by objects that an application no longer needs is known as garbage collection. This is accomplished in.NET Core via a highly developed garbage collector that operates in the background, regularly searching the managed heap for things that have not been referenced and recovering their memory. Fundamentally, managed heap memory—the memory used by C# applications to store instantiated objects—is allocated and released by Garbage Collection in.NET Core.

Garbage Collection Components in.NET Core

  • Managed Heap: The managed heap is a section of memory set aside for the purpose of storing application-created objects by the Common Language Runtime (CLR). The managed heap in.NET Core is separated into three generations: Gen0, Gen1, and Gen2. Initially assigned to Gen0, objects are promoted to higher generations as long as they survive garbage collection cycles.
  • Garbage Collector: In.NET Core, the garbage collector is the main element in charge of memory reclamation. It runs in the background, periodically scanning the managed heap to locate and retrieve things that the application can no longer access. To maximize collection efficiency and reduce interference with application performance, the garbage collector employs a variety of algorithms and heuristics.
  • Finalization Queue: Finalization is supported by.NET Core, enabling objects to carry out cleanup operations prior to being picked up by garbage collectors. The finalization queue is a dedicated queue for objects that need to be finalized. In order to guarantee that their finalizers are called prior to their reclamation, objects in the finalization queue are handled independently during garbage collection.
  • Large Object Heap (LOH): In.NET Core, large objects (usually those greater than 85,000 bytes) are stored in the Large Object Heap, a separate section of the managed heap. Large items are treated differently by the trash collector due to their size in order to reduce fragmentation and enhance performance.

Three basic steps are involved in the operation of this automated process:

  • Marking: To determine which objects are still in use, the GC begins by iterating through all object references, beginning at the roots.
  • Relocating: The GC compacts the heap by moving active objects closer to one another after detecting them, improving speed and memory layout. It modifies references in parallel to take into account the updated memory addresses.
  • Clearing: The last phase involves the GC freeing up memory that has been occupied by objects that are no longer referenced, freeing up resources for new allocations.

Including Future Generations to Increase Efficiency
Generational memory management is one of the main techniques that.NET GC uses to increase efficiency. The managed heap is divided into three generations, each of which serves different object categories:

  • Gen 0: This segment accommodates short-lived objects, which typically have a transient lifespan within the application. As a result, a significant portion of memory reclamation occurs in this generation.
  • Gen 1: Positioned as a buffer between short-lived and long-lived objects, Generation 1 serves to segregate objects based on their longevity. Objects surviving multiple garbage collections in Gen 0 are promoted to Gen 1.
  • Gen 2: Comprising long-lived objects, Generation 2 hosts entities expected to persist throughout the application's lifecycle. Garbage collections within this segment are less frequent due to the enduring nature of its occupants.

Conclusion

Within the.NET Core ecosystem, garbage collection is a fundamental component of memory management because it provides an automated solution to the challenges associated with memory allocation and deallocation. Through an understanding of GC's inner workings and an embrace of generational memory management, developers may create apps that are more resilient to changing workloads and perform better overall. The finalization queue, managed heap, garbage collector, and other parts of.NET Core integrate flawlessly to automate memory management and offer a dependable execution environment for.NET Core programs.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in