How can I stop a Gurobi solve in JuMP in real time (like Ctrl+C) and still keep the solution?

Please press once.

e.g.

import JuMP, Gurobi, SparseArrays
N = 50 # 754 seconds
Q = 1. * SparseArrays.dropzeros(
    SparseArrays.spdiagm(
        [i => rand(-9:9, N - i) for i in 0:div(N, 2)]...
    )
)
m = JuMP.Model();
JuMP.@variable(m, rand(-7:-3) <= y[1:N] <= rand(3:7));
JuMP.@variable(m, rand(-9:-5) <= x[1:N] <= rand(5:9));
JuMP.@objective(m, Min, (x'Q)y);
JuMP.set_optimizer(m, Gurobi.Optimizer)
JuMP.optimize!(m); JuMP.solution_summary(m)

test:

julia> JuMP.optimize!(m); JuMP.solution_summary(m)
Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - "Debian GNU/Linux 12 (bookworm)")

CPU model: AMD EPYC 7763 64-Core Processor, instruction set [SSE2|AVX|AVX2]
Thread count: 128 physical cores, 256 logical processors, using up to 32 threads

Optimize a model with 0 rows, 100 columns and 0 nonzeros
Model fingerprint: 0x8f14f73d
Model has 927 quadratic objective terms
Coefficient statistics:
  Matrix range     [0e+00, 0e+00]
  Objective range  [0e+00, 0e+00]
  QObjective range [2e+00, 2e+01]
  Bounds range     [3e+00, 9e+00]
  RHS range        [0e+00, 0e+00]

Continuous model is non-convex -- solving as a MIP

Found heuristic solution: objective 1972.0000000
Found heuristic solution: objective -90.0000000
Presolve removed 0 rows and 5 columns
Presolve time: 0.01s
Presolved: 921 rows, 1016 columns, 2763 nonzeros
Found heuristic solution: objective -1365.000000
Variable types: 0 continuous, 1016 integer (1016 binary)

Root relaxation: objective -1.668040e+05, 396 iterations, 0.00 seconds (0.00 work units)

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0 -166804.00    0   95 -1365.0000 -166804.00      -     -    0s
H    0     0                    -31392.00000 -166804.00   431%     -    0s
H    0     0                    -42554.00000 -166804.00   292%     -    0s
H    0     0                    -44102.00000 -166804.00   278%     -    0s
H    0     0                    -45200.00000 -166804.00   269%     -    0s
     0     0 -154413.00    0  127 -45200.000 -154413.00   242%     -    0s
     0     0 -148261.00    0  145 -45200.000 -148261.00   228%     -    0s
     0     0 -148189.50    0  145 -45200.000 -148189.50   228%     -    0s
     0     2 -148189.50    0  145 -45200.000 -148189.50   228%     -    0s
H   31    64                    -46492.00000 -134496.00   189%   132    0s
H   50    64                    -47016.00000 -133618.25   184%   121    0s
H   51    64                    -47256.00000 -133215.88   182%   121    0s
H  511   542                    -48548.00000 -129601.50   167%  82.7    0s
H  545   689                    -48697.00000 -129601.50   166%  82.5    0s
H  572   689                    -49412.00000 -129601.50   162%  82.1    0s
* 1639  1572              40    -50381.00000 -128661.00   155%  69.6    0s
* 1640  1572              40    -50573.00000 -128661.00   154%  69.6    0s
* 1642  1572              38    -54946.00000 -128661.00   134%  69.5    0s
H 1794  1693                    -57200.00000 -128596.63   125%  69.3    0s
H 1990  1823                    -57215.00000 -128479.25   125%  69.1    0s
H 1994  1823                    -57981.00000 -128479.25   122%  69.1    0s
H 2020  1823                    -58562.00000 -128479.25   119%  69.0    0s
H 2083  1823                    -59088.00000 -128479.25   117%  69.1    0s
H 2160  1985                    -60250.00000 -128479.25   113%  69.2    0s
H 2340  2000                    -60908.00000 -127924.75   110%  69.3    0s
H 2755  2220                    -62933.00000 -127388.67   102%  68.3    0s
H 3141  2478                    -63149.00000 -126541.00   100%  67.1    0s
 58996 26304 -84114.500   28  813 -63149.000 -94544.362  49.7%  42.3    5s
^C
Cutting planes:
  Gomory: 42
  Flow cover: 340
  Zero half: 366
  RLT: 7
  BQP: 4

Explored 59003 nodes (2510577 simplex iterations) in 7.97 seconds (10.92 work units)
Thread count was 32 (of 256 available processors)

Solution count 10: -63149 -62933 -60908 ... -54946

Solve interrupted
Best objective -6.314900000000e+04, best bound -8.731000000000e+04, gap 38.2603%

User-callback calls 119146, time in user-callback 0.02 sec
solution_summary(; result = 1, verbose = false)
├ solver_name          : Gurobi
├ Termination
│ ├ termination_status : INTERRUPTED
│ ├ result_count       : 10
│ ├ raw_status         : Optimization was terminated by the user.
│ └ objective_bound    : -8.73100e+04
├ Solution (result = 1)
│ ├ primal_status        : FEASIBLE_POINT
│ ├ dual_status          : NO_SOLUTION
│ ├ objective_value      : -6.31490e+04
│ └ relative_gap         : 3.82603e-01
└ Work counters
  ├ solve_time (sec)   : 7.99659e+00
  ├ simplex_iterations : 2510577
  ├ barrier_iterations : 0
  └ node_count         : 59003

julia> JuMP.value.(x)
50-element Vector{Float64}:

This is not correct either, you can refer to The Julia REPL · The Julia Language