diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -232,6 +232,14 @@ returned as a string.  The default is @code{False}, in which case the
 return value is @code{None}.  If @var{to_string} is @code{True}, the
 @value{GDBN} virtual terminal will be temporarily set to unlimited width
 and height, and its pagination will be disabled; @pxref{Screen Size}.
+
+The @var{release_gil} flag specifies whether @value{GDBN} ought to
+release the Python GIL before executing the command.  This is useful
+in multi-threaded Python programs where by default the Python
+interpreter will acquire the GIL and lock other threads from
+executing.  After the command has completed executing in @value{GDBN}
+the Python GIL is reacquired. This flag must be a boolean value.  If
+omitted, it defaults to @code{False}.
 @end defun
 
 @findex gdb.breakpoints
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -148,6 +148,8 @@ typedef int Py_ssize_t;
 #define PyGILState_Release(ARG) ((void)(ARG))
 #define PyEval_InitThreads()
 #define PyThreadState_Swap(ARG) ((void)(ARG))
+#define PyEval_SaveThread() ((void)(ARG))
+#define PyEval_RestoreThread(ARG) ((void)(ARG))
 #define PyEval_ReleaseLock()
 #endif
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -556,12 +556,16 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
 {
   const char *arg;
   PyObject *from_tty_obj = NULL, *to_string_obj = NULL;
-  int from_tty, to_string;
-  static const char *keywords[] = { "command", "from_tty", "to_string", NULL };
+  int from_tty, to_string, release_gil;
+  static const char *keywords[] = {"command", "from_tty", "to_string", "release_gil", NULL };
+  PyObject *release_gil_obj = NULL;
+  /* Initialize it just to avoid a GCC false warning.  */
+  PyThreadState *state = NULL;
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg,
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg,
 					&PyBool_Type, &from_tty_obj,
-					&PyBool_Type, &to_string_obj))
+					&PyBool_Type, &to_string_obj,
+					&PyBool_Type, &release_gil_obj))
     return NULL;
 
   from_tty = 0;
@@ -582,6 +586,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
       to_string = cmp;
     }
 
+  release_gil = 0;
+  if (release_gil_obj)
+    {
+      int cmp = PyObject_IsTrue (release_gil_obj);
+      if (cmp < 0)
+	return NULL;
+      release_gil = cmp;
+    }
+
   std::string to_string_res;
 
   TRY
@@ -602,6 +615,13 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
 
       counted_command_line lines = read_command_lines_1 (reader, 1, nullptr);
 
+      /* In the case of long running GDB commands, allow the user to
+	 release the Python GIL acquired by Python.  Restore the GIL
+	 after the command has completed before handing back to
+	 Python.  */
+      if (release_gil)
+	state = PyEval_SaveThread();
+
       scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
       scoped_restore save_uiout = make_scoped_restore (&current_uiout);
@@ -617,10 +637,22 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
 							    from_tty);
       else
 	execute_control_commands (lines.get (), from_tty);
+
+      /* Reacquire the GIL if it was released earlier.  */
+      if (release_gil)
+	PyEval_RestoreThread (state);
     }
   CATCH (except, RETURN_MASK_ALL)
     {
-      GDB_PY_HANDLE_EXCEPTION (except);
+      if (except.reason < 0)
+	{
+	  /* Reacquire the GIL if it was released earlier.  */
+	  if (release_gil)
+	    PyEval_RestoreThread (state);
+
+	  gdbpy_convert_exception (except);
+	  return NULL;
+	}
     }
   END_CATCH
 
