Async Exempel - Filsökning 🔍
💡 Vad gör detta exempel?
Filsökning som är snabb som blixten! ⚡
Tänk dig att du ska söka igenom tusentals filer efter ett specifikt ord. Vanligt sätt = kolla en fil i taget (tråkigt och långsamt). Async-sätt = kolla många filer samtidigt (coolt och snabbt)!
Det är som skillnaden mellan att äta popcorn ett korn i taget vs att ta en näve - mycket effektivare!
🎯 TL;DR
Parallell filsökning med Task.WhenAll. Istället för 100 sekunder tar det bara 5 sekunder!
Exempel - Sökning av filer
Vi skapar ett async-drivet sökverktyg som letar genom alla .cs-filer parallellt! 🚀
🟢 Grundläggande async-filsökning
static class Program
{
// Async Main - modern C-Sharp!
static async Task Main()
{
var searcher = new FileSearcher();
var sourceFolder = @"C:\source";
Console.WriteLine("🔍 Söker efter 'async' i alla .cs filer...");
var stopwatch = Stopwatch.StartNew();
// ASYNC call - blockerar inte!
var results = await searcher.SearchFilesAsync(
sourceFolder,
"*.cs",
"async"
);
stopwatch.Stop();
Console.WriteLine($"\n✅ Hittade {results.Count} matchningar på {stopwatch.ElapsedMilliseconds}ms:\n");
foreach (var result in results)
{
Console.WriteLine($"📄 {result}");
}
}
}
🟡 FileSearcher-klassen med error handling
public class FileSearcher
{
public async Task<List<SearchResult>> SearchFilesAsync(
string searchPath,
string pattern,
string searchText)
{
try
{
// Hämta alla filer som matchar pattern
var files = Directory.GetFiles(searchPath, pattern, SearchOption.AllDirectories);
Console.WriteLine($"📁 Hittade {files.Length} filer att söka igenom");
// Skapa en task för varje fil (PARALLELLT!)
var searchTasks = files.Select(file => SearchSingleFileAsync(file, searchText));
// Vänta på att ALLA tasks blir klara samtidigt
var results = await Task.WhenAll(searchTasks);
// Filtrera bort null-resultat
return results.Where(result => result != null).ToList();
}
catch (DirectoryNotFoundException)
{
Console.WriteLine($"❌ Mappen {searchPath} finns inte!");
return new List<SearchResult>();
}
catch (UnauthorizedAccessException)
{
Console.WriteLine($"🔒 Ingen åtkomst till {searchPath}");
return new List<SearchResult>();
}
}
private async Task<SearchResult?> SearchSingleFileAsync(string filePath, string searchText)
{
try
{
// Läs filen asynkront - blockerar inte!
var lines = await File.ReadAllLinesAsync(filePath);
// Sök efter text och hitta radnummer
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(searchText, StringComparison.OrdinalIgnoreCase))
{
return new SearchResult
{
FilePath = filePath,
LineNumber = i + 1,
LineContent = lines[i].Trim(),
SearchTerm = searchText
};
}
}
return null; // Inget hittat
}
catch (IOException ex)
{
Console.WriteLine($"⚠️ Kunde inte läsa {filePath}: {ex.Message}");
return null;
}
}
}
public class SearchResult
{
public string FilePath { get; set; }
public int LineNumber { get; set; }
public string LineContent { get; set; }
public string SearchTerm { get; set; }
public override string ToString()
{
var fileName = Path.GetFileName(FilePath);
return $"{fileName}:{LineNumber} → {LineContent}";
}
}
### 🔴 Avancerad version med progress tracking
```csharp
public class AdvancedFileSearcher
{
public async Task<List<SearchResult>> SearchWithProgressAsync(
string searchPath,
string pattern,
string searchText,
IProgress<SearchProgress> progress = null)
{
var files = Directory.GetFiles(searchPath, pattern, SearchOption.AllDirectories);
var results = new List<SearchResult>();
var processed = 0;
// Begränsa antal samtidiga tasks för att inte överbelasta systemet
var semaphore = new SemaphoreSlim(Environment.ProcessorCount * 2); // 2x CPU kärnor
var tasks = files.Select(async file =>
{
await semaphore.WaitAsync(); // Vänta på ledig "slot"
try
{
var result = await SearchSingleFileAsync(file, searchText);
// Uppdatera progress
var currentProcessed = Interlocked.Increment(ref processed);
progress?.Report(new SearchProgress
{
ProcessedFiles = currentProcessed,
TotalFiles = files.Length,
CurrentFile = Path.GetFileName(file)
});
return result;
}
finally
{
semaphore.Release(); // Frigör "slot"
}
});
var allResults = await Task.WhenAll(tasks);
return allResults.Where(r => r != null).ToList();
}
}
public class SearchProgress
{
public int ProcessedFiles { get; set; }
public int TotalFiles { get; set; }
public string CurrentFile { get; set; }
public double PercentComplete => (double)ProcessedFiles / TotalFiles * 100;
public override string ToString()
{
return $"[{PercentComplete:F1}%] {ProcessedFiles}/{TotalFiles} - {CurrentFile}";
}
}
// Användning med progress:
static async Task SearchWithProgressExample()
{
var searcher = new AdvancedFileSearcher();
var progress = new Progress<SearchProgress>(p =>
{
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write($"🔄 {p}");
});
var results = await searcher.SearchWithProgressAsync(
@"C:\LargeCodebase",
"*.cs",
"async",
progress
);
Console.WriteLine($"\n✅ Klart! Hittade {results.Count} matchningar");
}
🚀 Så fungerar magin
Task.WhenAll- Kör alla filsökningar parallelltSemaphoreSlim- Begränsar antal samtidiga tasks (undvik överbelastning)Progress<T>- Visar framsteg i realtidFile.ReadAllLinesAsync- Läser filer utan att blockera- Error handling - Hanterar filer som inte kan läsas
📊 Prestanda-jämförelse
SYNC (Sekventiell):
📄 Fil 1: 100ms
📄 Fil 2: 100ms
📄 Fil 3: 100ms
📄 Fil 4: 100ms
TOTALT: 400ms ⏰
ASYNC (Parallell):
📄📄📄📄 Alla filer: ~100ms
TOTALT: 100ms ⚡
SPEED UP: 4x snabbare! 🚀
📚 Sammanfattning
Async filsökning = superkraft för stora kodbasers:
- Parallell exekvering med
Task.WhenAll - Resource management med
SemaphoreSlim - Progress tracking för användarupplevelse
- Robust error handling för produktionskod
- 4x+ snabbare än sekventiell sökning
🎯 Övningar
- 🟢 Grundläggande: Modifiera koden för att söka efter flera termer samtidigt
- 🟡 Utmanande: Lägg till filtrering efter filstorlek och datum
- 🔴 Expert: Implementera fuzzy search och regex-stöd
😄 Obligatoriska dad joke
Varför är async-filsökning som en sökmotor?
För att båda är parallella och hittar saker asynkront! 🔍