# Poll Mode (/sdks/csharp/how-to/queues/poll-mode)



## Overview [#overview]

**Poll mode** is a pull-based way to consume queue messages: the consumer decides exactly when to ask for work and how much, instead of holding an open stream the broker pushes into. That control matters for batch jobs, cron-triggered workers, and any consumer that only runs intermittently and would rather ask "is there anything for me?" than keep a subscription alive.

A single call to `PollAsync` on a `QueueDownstreamReceiver` sends a channel, `MaxMessages`, and `WaitTimeoutSeconds`; the broker holds the request open as a long poll and returns once enough messages are available or the timeout elapses, so the call never spins on an empty queue. `AutoAck = true` settles the whole batch on delivery, with no separate `AckAsync` step.

**Gotchas:** auto-ack removes messages the instant they're delivered — a crash mid-processing loses them, so set `AutoAck = false` and ack manually when work can fail; the timeout bounds latency, not throughput, so a small `MaxMessages` on a busy queue means many round trips; and check `batch.HasMessages` before assuming work is done — an empty batch just means nothing arrived in that window, not that the queue is drained for good.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* C# SDK installed (`dotnet add package KubeMQ.SDK.CSharp`)

## Code [#code]

```csharp title="Program.cs"
// KubeMQ .NET SDK — QueuesStream: Poll Mode
//
// This example demonstrates using the poll-based queue consumption pattern
// with the downstream receiver API. Messages are fetched on demand, giving
// the consumer full control over when to receive and process messages.
//
// Prerequisites:
//   - KubeMQ server running on localhost:50000
//   - dotnet run

using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Queues;
using System.Text;

await using var client = new KubeMQClient(new KubeMQClientOptions
{
    ClientId = "csharp-queues-poll-mode-client",
});
await client.ConnectAsync();

Console.WriteLine("Connected to KubeMQ server");

for (var i = 1; i <= 10; i++)
{
    await client.SendQueueMessageAsync(new QueueMessage
    {
        Channel = "csharp-queues.poll-mode",
        Body = Encoding.UTF8.GetBytes($"Poll Message #{i}")
    });
}

Console.WriteLine("Sent 10 messages");

await using var receiver = await client.CreateQueueDownstreamReceiverAsync();

var batchNum = 1;
while (true)
{
    var batch = await receiver.PollAsync(new QueuePollRequest
    {
        Channel = "csharp-queues.poll-mode",
        MaxMessages = 3,
        WaitTimeoutSeconds = 5,
        AutoAck = true,
    });

    if (!batch.HasMessages)
    {
        Console.WriteLine("No more messages. Exiting poll loop.");
        break;
    }

    Console.WriteLine($"Poll batch #{batchNum}: received {batch.Messages.Count} messages");
    foreach (var msg in batch.Messages)
    {
        Console.WriteLine($"  {Encoding.UTF8.GetString(msg.Body.Span)}");
    }

    batchNum++;
}

Console.WriteLine("Done.");

```

## How It Works [#how-it-works]

* The `while (true)` loop calls `PollAsync` repeatedly with `MaxMessages = 3`. Each call fetches up to 3 messages; `WaitTimeoutSeconds = 5` keeps the poll from spinning when the queue is empty.
* `AutoAck = true` means the broker acknowledges all returned messages automatically — no manual `AckAsync` is needed. The loop exits when `batch.HasMessages` is false.
* This pattern gives the consumer full control over concurrency and batch size, unlike push-based subscriptions.
* Reduce `MaxMessages` to 1 for strict ordered processing; increase for higher throughput at the cost of larger in-flight windows.

## Related [#related]

* [Pattern overview](/learn/queues/getting-started)
* [C# SDK Reference](/sdks/csharp/reference)
* [Send & Receive](/sdks/csharp/tutorials/send-receive)
* [Ack All](/sdks/csharp/how-to/queues/ack-all)
