You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

高效并发数据结构:等待计算结果或超时方案咨询

Matching Concurrent Data Structures for Your Blocking Web Request Use Case

Hey there, let's break down your problem clearly: you have web requests holding a unique key, needing to fetch a corresponding computed result, block up to n seconds if the result isn't ready yet, and return a "not available" message on timeout. You want to avoid reinventing the wheel with existing, efficient concurrent patterns—great call, that's where the right data structures shine.

Core Requirements Recap

First, let's align on what we need from the solution:

  • Map unique keys to their "pending/ready" result state
  • Support multiple concurrent requests waiting on the same key's result
  • Safe, efficient blocking with configurable timeout
  • Prevent redundant computation for the same key (critical to avoid wasted resources)

1. Single-Machine: ConcurrentHashMap + Condition Variables (Java)

If you're working in a single JVM environment, this is a classic, low-level but highly customizable approach:

  • Use a ConcurrentHashMap<String, ResultHolder> where the key is your unique request key, and ResultHolder is a custom class holding:
    • The computed result (nullable until ready)
    • A Lock and Condition object to manage waiting threads
    • A state flag (e.g., PENDING, READY, TIMED_OUT)
  • When a request comes in:
    1. Use map.putIfAbsent(key, new ResultHolder()) to atomically create a holder if it doesn't exist (prevents duplicate computation triggers)
    2. Acquire the lock on the ResultHolder
    3. Wait on the Condition with a timeout of n seconds
    4. On wakeup, check the state:
      • If READY: return the result
      • If PENDING/TIMED_OUT: return "暂未获取到结果"
  • When the computation for a key finishes:
    1. Fetch the ResultHolder from the map
    2. Acquire the lock, update the result and state to READY
    3. Call condition.signalAll() to wake all waiting requests

Pro Tip: Add cleanup logic for timed-out or completed keys to avoid memory bloat (e.g., remove entries from the map after result is fetched or timeout expires).

2. Single-Machine: Guava LoadingCache (Java)

If you want a battle-tested, out-of-the-box solution that avoids rolling your own synchronization, Google Guava's LoadingCache is perfect:

  • Configure the cache with a maximum size (to limit memory usage) and a custom loader that triggers the computation for a key if it's not present
  • Use loadingCache.get(key, n, TimeUnit.SECONDS)—this method will block until the result is ready or the timeout hits
  • The cache automatically handles concurrent requests for the same key: only one computation will run, and all waiting requests get the result once it's done
  • You can also set expiration policies to automatically evict old results

Example snippet to illustrate the pattern:

LoadingCache<String, ComputedResult> resultCache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .build(new CacheLoader<String, ComputedResult>() {
        @Override
        public ComputedResult load(String key) throws Exception {
            // Trigger your computation logic here
            return computeResultForKey(key);
        }
    });

// In your web request handler:
try {
    ComputedResult result = resultCache.get(key, n, TimeUnit.SECONDS);
    return result;
} catch (TimeoutException e) {
    return "暂未获取到结果";
} catch (ExecutionException e) {
    // Handle computation errors
}

3. Distributed Scenario: Redis Pub/Sub + Key Expiration

If your system runs across multiple servers (distributed), a local data structure won't work. Redis provides a clean solution:

  • When a request comes in:
    1. Check if the key exists in Redis with GET key—if yes, return the result
    2. If not, subscribe to a dedicated channel for that key (e.g., result:{key})
    3. Wait up to n seconds for a message on the channel; if no message arrives, unsubscribe and return "暂未获取到结果"
  • When computation finishes:
    1. Store the result in Redis with SET key result EX <expiry-time> (to auto-clean old results)
    2. Publish the result to the channel result:{key} to notify all waiting requests

Bonus: Use Redis' BLPOP (blocking list pop) as an alternative to pub/sub—store pending result notifications in a list, and have requests block on popping from the list with a timeout.


Key Considerations to Avoid Pitfalls

  • Redundant Computation: Always use atomic operations (like putIfAbsent in Java, or Redis SETNX) to ensure only one computation runs per key
  • Memory/Disk Cleanup: Configure expiration for cached results to prevent resource leaks
  • Timeout Precision: Be aware that some blocking mechanisms might have minor delays (e.g., OS thread scheduling), so adjust timeout values if strict precision is needed
  • Error Handling: Don't forget to handle cases where the computation fails—you'll want to notify waiting requests or clean up the pending state

内容的提问来源于stack exchange,提问作者Mannie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:02:35