c#题例-2025-08-02 09:52:13
日期: 2025-08-02 分类: AI写作 15次阅读
当然可以!下面是一道**专家级别**的 C# 程序员逻辑面试题,它不仅考察对 C# 语言特性的掌握,还涉及并发编程、线程安全、异步编程和设计模式的理解。
---
### 🧠 面试题:实现一个线程安全且支持异步的缓存系统(Cache)
#### 📌 题目描述:
你需要实现一个通用的缓存系统 `ConcurrentAsyncCache
1. **支持异步加载数据**:如果缓存中没有对应的键,则调用一个传入的异步工厂函数 `Func
2. **线程安全**:多个线程或任务可以同时访问该缓存而不会出现数据竞争。
3. **防止缓存击穿**:当多个线程同时请求一个缓存未命中项时,只允许一个线程执行加载操作,其余线程等待其完成。
4. **支持过期机制(可选加分)**:每个缓存项可以设置生存时间(TTL),自动清理过期项。
5. **支持清除缓存项**。
6. **异步清理过期缓存项(可选加分)**。
#### 📌 接口定义(简化版):
```csharp
public interface IAsyncCache
{
Task
void Remove(TKey key);
}
```
#### 📌 示例使用:
```csharp
var cache = new ConcurrentAsyncCache
var result1 = await cache.GetOrAddAsync("key1", async (k, ct) =>
{
await Task.Delay(1000, ct); // 模拟耗时操作
return "value1";
}, CancellationToken.None);
var result2 = await cache.GetOrAddAsync("key1", (k, ct) => throw new Exception("Should not be called again!"), CancellationToken.None);
// result2 应该等于 "value1"
```
---
### 🧩 考察点:
- 对 `ConcurrentDictionary
- 对 `TaskCompletionSource
- 异步编程与 `CancellationToken` 的结合。
- 线程安全设计与并发控制。
- 可选:定时清理机制(使用 `Timer` 或后台任务)。
- 可选:缓存过期策略(LRU、TTL 等)。
---
### ✅ 参考答案(简化实现):
```csharp
public class ConcurrentAsyncCache
{
private readonly ConcurrentDictionary
private readonly TimeSpan _defaultTtl;
public ConcurrentAsyncCache(TimeSpan? defaultTtl = null)
{
_defaultTtl = defaultTtl ?? Timeout.InfiniteTimeSpan;
}
public async Task
TKey key,
Func
TimeSpan? ttl = null,
CancellationToken ct = default)
{
if (_cache.TryGetValue(key, out var entry) && !entry.IsExpired())
{
return entry.Value;
}
var tcs = new TaskCompletionSource
var newEntry = new CacheEntry(default!, ttl ?? _defaultTtl);
var added = _cache.TryAdd(key, newEntry);
if (!added)
{
// 如果已经有其他线程在加载,等待它完成
entry = _cache[key];
if (!entry.IsExpired())
{
return entry.Value;
}
// 如果已经过期,尝试替换
if (!_cache.TryUpdate(key, newEntry, entry))
{
// 如果替换失败,说明其他线程已经更新了缓存
entry = _cache[key];
if (!entry.IsExpired())
{
return entry.Value;
}
else
{
throw new InvalidOperationException("Conflict during cache update.");
}
}
}
try
{
var value = await factory(key, ct).ConfigureAwait(false);
newEntry.SetValue(value);
tcs.SetResult(value);
return value;
}
catch (Exception ex)
{
_cache.TryRemove(key, out _);
tcs.SetException(ex);
throw;
}
}
public void Remove(TKey key)
{
_cache.TryRemove(key, out _);
}
private class CacheEntry
{
private TValue _value;
private readonly object _lock = new();
private DateTime _created;
private readonly TimeSpan _ttl;
public CacheEntry(TValue value, TimeSpan ttl)
{
_value = value;
_created = DateTime.UtcNow;
_ttl = ttl;
}
public bool IsExpired()
{
if (_ttl == Timeout.InfiniteTimeSpan)
return false;
return DateTime.UtcNow - _created > _ttl;
}
public TValue Value => _value;
public void SetValue(TValue value)
{
lock (_lock)
{
_value = value;
_created = DateTime.UtcNow;
}
}
}
}
```
---
### 🧪 拓展问题(面试追问):
- 如何测试这个缓存的线程安全性?
- 如果缓存的数据很大,如何优化内存使用?
- 如何支持 LRU 缓存策略?
- 如何避免死锁在异步代码中?
- 如何监控缓存命中率?
---
如果你需要我为这道题写一个完整的单元测试示例或扩展功能(如 TTL 自动清理),我也可以继续补充。希望这道题能帮助你筛选出真正有经验的 C# 工程师!
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:AI写作
精华推荐